2015-04-05 30 views
0

如何避免屬性獲取器中的遞歸調用?這是我簡單的代碼屬性獲取器中使用的遞歸表達式

public class UploadAttribute : Attribute 
{ 
    private Type _resourceType; 
    private string _select; 
    private string _change; 
    private string _remove; 

    public Type ResourceType 
    { 
     get { return _resourceType; } 
     set { _resourceType = value; } 
    } 

    public string Select 
    { 
     get { return GetResourceText(m => m.Select, "Select..."); } 
     set { _select = value; } 
    } 

    public string Change 
    { 
     get { return GetResourceText(m => m.Change, "Change..."); } 
     set { _change = value; } 
    } 

    public string Remove 
    { 
     get { return GetResourceText(m => m.Remove, "Remove"); } 
     set { _remove = value; } 
    } 

    private string GetResourceText(Expression<Func<UploadAttribute, string>> expression, string @default) 
    { 
     var value = expression.Compile().Invoke(this); // here .net is creating new UploadAttribute instance and use it for expression fnc 
     var result = value ?? @default; 

     if (_resourceType != null && !string.IsNullOrEmpty(value)) 
     { 
      ResourceManager rm = new ResourceManager(_resourceType); 
      try 
      { 
       result = rm.GetString(value); 
      } 
      catch 
      { 
       // if string wasn't found in resource file than use what user specify; don't by big brother. 
      } 
     } 

     return result; 
    } 
} 

但是,如果你看一下方法GetResourceText,有行,我需要編譯並調用表達以獲取給定的屬性值。不幸的是,這個操作創建了UploadAttribute的新實例。在那一刻,.net通過所有屬性並調用getter,如果我沒有弄錯,並且在getter中.net編譯並調用表達式來獲取值或給定屬性,再次一次又一次地發生到StackOverlowException。 你能告訴我如何避免這種行爲,但簡單的解決方案?

編輯:該類的作用是爲按鈕提供標題 - 用戶設置了哪些用戶可以使用資源管理器中的多語言標題。 在上面的例子中,按鈕選擇從資源翻譯,對於更改按鈕,使用默認文本「更改...」和刪除按鈕標題「銷燬此@ &#!」。因此,如果用戶沒有指定屬性值,那麼應用程序使用默認文本,否則嘗試在資源中查找文本,並且如果找到匹配,則使用來自資源的文本,否則使用用戶設置的內容。

[Required] 
[Upload(ResourceType = typeof(Resource), Select = "UploadSelect", Remove = "Destroy this @&#!")] 
public HttpPostedFileBase Logo { get; set; } 
+0

看來,你的屬性的屬性值設置爲兩種不同的價值觀: (1)用於從資源中檢索標題字符串的資源鍵,(2)將被用作標題本身的文字字符串。這絕對不是一個乾淨的方式,儘管我絕不會選擇像這樣的脆弱設計解決方案,但我想它可以工作。根據我對你的目的的新理解,我會更新我的答案。 – Alex 2015-04-05 22:49:17

+0

對於我來說,這個代碼應該做什麼並不清楚。也許你可以澄清你的意圖,然後你可能會得到一個滿足你的要求的答案:) – MBoros 2015-04-06 10:59:05

+0

@SebastianBusek你能指出是否有任何答案充分解決了你的問題,或者指出你有什麼問題。或者,如果您找到一種以不同方式自己解決問題的方法,請將其作爲答案。 – Alex 2015-04-13 12:13:55

回答

1

我很漂亮的轉儲。它根本不會創建新的實例;我問了一會兒,我就回答了我的問題。

return GetResourceText(m => m.Select, "Select...");是遞歸沒有結束,但return GetResourceText(m => m._select, "Select...");不是,因爲我不是再次調用方法GetResourceText。

對不起男生的傻問題。

2

看來你試圖實現的是有一些方法來初始化這些屬性,如果他們沒有明確設置。你這樣做的方式是行不通的。

m => m.Remove類型表達式將導致在無限遞歸中再次調用屬性getter,直到發生堆棧溢出。

您可以使用lazy結構,如下圖所示。它的工作原理如下:

  1. 如果用戶沒有爲屬性指定值,那麼當調用屬性getter時,它將返回硬編碼的默認值。
  2. 如果用戶爲某個屬性指定了一個值,那麼該值將首先用作關鍵字,以試圖從資源中檢索相應的字符串值。如果未找到該資源,則將其用作該屬性的值,前提是該資源不爲空,否則將回退到硬編碼的默認值。

請注意,這種屬性屬性值的雙重用途導致了一個相當脆弱的設計解決方案。如果沒有找到具有「UploadSelect」鍵的資源,那將成爲該按鈕上的標題。

public class UploadAttribute : Attribute 
{ 
    private static readonly string kSelectDefaultCaption = "Select..."; 
    private static readonly string kChangeDefaultCaption = "Change..."; 
    private static readonly string kRemoveDefaultCaption = "Remove..."; 

    private Type _resourceType; 
    private Lazy<string> _select = new Lazy<string>(() => kSelectDefaultCaption); 
    private Lazy<string> _change = new Lazy<string>(() => kChangeDefaultCaption); 
    private Lazy<string> _remove = new Lazy<string>(() => kRemoveDefaultCaption); 

    public Type ResourceType 
    { 
     get { return _resourceType; } 
     set { _resourceType = value; } 
    } 

    public string Select 
    { 
     get { return _select.Value; } 
     set { _select = new Lazy<string>(() => GetResourceText(value, kSelectDefaultCaption)); } 
    } 

    public string Change 
    { 
     get { return _change.Value; } 
     set { _change = new Lazy<string>(() => GetResourceText(value, kChangeDefaultCaption)); } 
    } 

    public string Remove 
    { 
     get { return _remove.Value; } 
     set { _remove = new Lazy<string>(() => GetResourceText(value, kRemoveDefaultCaption)); } 
    } 

    private string GetResourceText(string key, string @default) 
    { 
     // initialize to default. 
     var result = @default; 
     if (_resourceType != null && !string.IsNullOrEmpty(key)) 
     { 
      // initialize to the value of the key, 
      // that could be a user supplied string literal 
      result = key; 

      // attempt to retrieve it from the resources. 
      ResourceManager rm = new ResourceManager(_resourceType); 
      try 
      { 
       result = rm.GetString(key); 
      } 
      catch 
      { 
       // could not retrieve key, using the key value as the result. 
      } 
     } 
     return result; 
    } 
} 
+0

謝謝Alex的回覆。如果我對代碼的理解很好,那麼用戶可以設置屬性值,但他/她無法使用本地化資源......但是,如果您替換了set {_select = new Lazy (()=>值); }(帶有'set {_select = new Lazy (()=> GetResourceText(value,「Select ...」)); }'它可以工作,我說得對嗎? – 2015-04-05 22:13:47

+0

@SebastianBusek我不確定我是否明白你的意思。上面的代碼是返回由用戶設置的值,以防用戶明確設置屬性值,如果未設置屬性,則從資源中提取值,返回「@」如果資源中沒有'key'的值,那麼'default'。如果這不是你想要的行爲,你能否更新你的問題來指定你想達到的目標? – Alex 2015-04-05 22:20:44

+0

您是否打算這些屬性擁有將用於從資源中檢索對應值的資源鍵值? – Alex 2015-04-05 22:25:21

1

的輕微修改Alex的答案,如果你想保持你的編譯時安全使用的屬性名稱:

public class UploadAttribute : Attribute 
{ 
    private Type _resourceType; 
    private Lazy<string> _select; 
    private Lazy<string> _change; 
    private Lazy<string> _remove; 

    UploadAttribute() 
    { 
     _select = new Lazy<string>(() => GetResourceText(m => m.Select, "Select...")); 
     _change = new Lazy<string>(() => GetResourceText(m => m.Change, "Change...")); 
     _remove = new Lazy<string>(() => GetResourceText(m => m.Remove, "Remove...")); 
    } 

    public Type ResourceType 
    { 
     get { return _resourceType; } 
     set { _resourceType = value; } 
    } 

    public string Select 
    { 
     get { return _select.Value; } 
     set { _select = new Lazy<string>(() => value); } 
    } 

    public string Change 
    { 
     get { return _change.Value; } 
     set { _change = new Lazy<string>(() => value); } 
    } 

    public string Remove 
    { 
     get { return _remove.Value; } 
     set { _remove = new Lazy<string>(() => value); } 
    } 
    private string GetResourceText(Expression<Func<UploadAttribute, string>> expression, string @default) 
    { 
     var result = @default; 
     var memberExpression = expression.Body as MemberExpression; 
     if (_resourceType != null && memberExpression != null) 
     { 
      ResourceManager rm = new ResourceManager(_resourceType); 
      try 
      { 
       result = rm.GetString(memberExpression.Member.Name); 
      } 
      catch 
      { 
       // if string wasn't found in resource file than use what user specify; don't by big brother. 
      } 
     } 
     return result; 
    } 
} 
相關問題