2009-06-15 32 views
1

我有一個自定義類(調用我的字段),它實現了幾個屬性。其中一個屬性是MaximumLength,它表示該值的最大長度。 Value屬性是一個對象,所以我可以設置爲字符串,int,double等等。然後我有一個類有多個Field類型的屬性。所有的Field屬性都在構造函數中初始化,只有Field.Value屬性可以寫入。如果試圖將Field.Value設置爲對於字段來說太長的值並實現INotifyPropertyChanged,我想要做的就是拋出一個錯誤。我的問題是Value屬性是泛型Field類的成員,我不知道如何獲取該類內的屬性名稱。如何獲取myClass類型的屬性的名稱?

一個例子:

public class Customer 
{ 
    private Field _firstName = new Field(typeof(string), 20); 

    public Field FirstName 
    { 
     get 
     { 
     return _firstName; 
     } 
    } 
} 

public class Field 
{ 
    private Type _type; 
    private int _maximumLength; 
    object _value; 

    public Field(Type type, int maximumLength) 
    { 
     _type = type; 
     _maximumLength = maximumLength; 
    }   

    public Object Value 
    { 
     get 
     { 
     return _value; 
     } 
     set 
     { 
     if (value.ToString().Length > _maximumLength) 
     { 
      throw(string.Format("{0} cannot exceed {1} in length.", property name, _maximumValue); 
     } 
     else 
     { 
      _value = value; 
      OnPropertyChanged(property name); 
     } 
     } 
    } 
} 

希望這是清楚。

+0

最接近你會得到這是實際屬性的上下文訪問,該領域本身不會知道它的起源,畢竟,它只是一個實例..上下文的事情 - 即..即時通訊訪問此屬性以得到這個字段,然後打開反射將是非常嚴格的 - 像WPF的依賴系統類似的系統證明,在這種情況下,沒有比簡單地以字符串形式複製名稱更好的方法..或者你可以看看面向方面的系統,比如postsharp,它可以讓你編譯時間重寫IL。 – meandmycode 2009-06-16 14:31:12

回答

0

在我看來,是的,可能有一種使用反射來獲取這些信息的方法,但我不確定這是否是一種萬無一失的方法,並且可能會讓您在稍後進行更麻煩的調試階段上。它似乎過分...聰明。

在我看來,根據我過去的實施情況,Fredrik和Micahtan正指出你的方向是正確的。你的Field類應該實現一個Name屬性,在實例化時設置。我可以指出這是一個好主意的一個原因是,這是微軟做這件事的方式。如果您查看任何可視設計器的生成代碼,控件將實現由設計者設置的Name屬性。如果在封面下面有一個可靠的方法來做到這一點,那麼你就不得不相信這會完成。

使用Name屬性的另一個好處是,它允許您爲您的屬性提供「英文」翻譯。即「First Name」而不是「FirstName」,這是對用戶界面友好的方法,並將用戶從你的實現中解耦出來(以使「解耦」這個詞超載)。

0

爲什麼不簡單地擴展Field類來保存名稱,並在構造函數中傳遞它呢?

private Field _firstName = new Field(typeof(string), "FirstName", 20); 

Field類是這樣的:

public class Field 
{ 
    private Type _type; 
    private int _maximumLength; 
    private string _name; 
    object _value; 

    public Field(Type type, string name, int maximumLength) 
    { 
     _type = type; 
     _maximumLength = maximumLength; 
     _name = name; 
    }   

    public Object Value 
    { 
     get 
     { 
     return _value; 
     } 
     set 
     { 
     if (value.ToString().Length > _maximumLength) 
     { 
      throw new SomeException(string.Format("{0} cannot exceed {1} in length.", _name, _maximumValue)); 
     } 
     else 
     { 
      _value = value; 
      OnPropertyChanged(_name); 
     } 
     } 
    } 
} 
+0

我曾考慮過這個問題,但對於我來說似乎是多餘的,因爲該屬性有一個名稱,如果我或其他人開始重命名屬性,則會增加一個步驟。如果我不能得到另一種方法,我可能會迴避。 – jac 2009-06-15 20:12:04

0

同意瓦特/弗雷德裏克。

但是,如果您希望在Field數據中具有某種類型安全性,也應該考慮使用泛型,即將Field類重新定義爲字段,其中存儲T _value而不是對象。

我還以爲你必須有不同的算法來長檢查不同類型(如字符串與數字等),以及空值檢查等

HTH。

0

我同意以上所述。該字段應具有自己的名稱屬性,該名稱屬性是用構造設置的。

我不同意用泛型實現這個。類型安全是一個很好的目標,但您可能還需要能夠存儲不同類型的Field對象的集合/列表

如果沒有通用類也實現通用接口,泛型不會讓你這樣做。

您可以實現一個共同的接口,但那麼你就需要值爲類型的對象,不是類型<牛逼>,讓你失去了仿製藥的性能優勢。

+0

@yshuditelu - 我誤解了問題,更新了答案。 – richardtallent 2009-06-15 20:26:44

+0

我同意,如果需要一個Field的集合,那麼可能泛型不是要走的路,或者確實需要添加接口來使通用實現工作。但是,如果Field僅僅用於封裝某些驗證邏輯,那麼我相信泛型可能是合適的並且是首選。 – 2009-06-15 20:47:07

0

我相信這就是你想要發生的事情。我將泛型添加到類中,而不是使用對象來添加類型安全性。我還添加了一個明確的異常類型,以便編譯,但Exception類型可以是任何需要字符串的類型。

public class Customer 
{ 
    private Field<string> _firstName = new Field<string>("FirstName", 20); 

    public Field<string> FirstName 
    { 
     get 
     { 
      return _firstName; 
     } 
    } 
} 

public class Field<T> 
{ 
    private int _maximumLength; 
    private T _value; 
    private string _propertyName; 

    public Field(string propertyName, int maximumLength) 
    { 
     _propertyName = propertyName; 
     _maximumLength = maximumLength; 
    } 

    public T Value 
    { 
     get 
     { 
      return _value; 
     } 
     set 
     { 
      if (value.ToString().Length > _maximumLength) 
      { 
       throw new ArgumentException(string.Format("{0} cannot exceed {1} in length.", _propertyName, _maximumLength)); 
      } 
      else 
      { 
       _value = value; 
       OnPropertyChanged(_propertyName); 
      } 
     } 
    } 
} 

編輯:

響應您的評論:「我想過這個問題,但它似乎是多餘的,因爲我的屬性有一個名稱,並增加了一個步驟,如果我或其他人開始重命名屬性我可能會迴避,但如果我不能得到另一種方法。「

我相信你所要求的是某種反射代碼,它會在從Field通知屬性更改時檢索Customer中的屬性名稱。我不相信有任何可以可靠地執行代碼的代碼做到這一點考慮:?

Field myFirstNameField = myCustomer.FirstName; 
myFirstNameField.Value = "X"; 

應在屬性名是什麼在此領域的單個實例,現在有兩個獨立的範圍內的兩個標識的或許有給拉了回來標識符引用您的對象的列表(的方式但如果可以的話,我不知道怎麼做),但你會如何選擇你想要哪一個?這是,如果你想實現PROPERT我建議使用一個構造函數「注入」的屬性名,其原因y以這種方式更改通知。

0

有辦法做你想做的,但他們都不是特別優雅或萬無一失。

舉例來說,你可以走在你的領域類的調用堆棧時,您檢測和錯誤情況,並假設它是通過現場的從對象檢索總是調用。這可能並不總是正確...

或者,你可以使用LINQ表達式初始化時,它的字段的名稱傳遞到Field對象。 Marc Gravell有technique that's described here。就你而言,這可能並不完美。

最終,你可能有一個更簡單,更復雜的實施富裕。

最後,你應該考慮這是否真的是複製的情況而不是不必要的耦合。爲什麼從你的代碼發出的消息要與其中的屬性或方法名稱綁定?如果你需要在將來的某個時候將你的代碼國際化,該怎麼辦?如果客戶希望消息更清晰或更緊密地與業務領域保持一致,您會願意重構代碼以適應這些變化嗎?

+0

走棧不起作用,因爲值屬性不是通過「named」屬性訪問的。這已經從堆棧中彈出,並且您無法從屬性中訪問它。另外,我認爲關鍵是實施INotifyPropertyChanged來爲它填充事件arg。 – 2009-06-16 02:43:33

0

這裏是要解決什麼你正在嘗試做的一種方式:

 Customer c1 = new Customer(); 
     c1.FirstName.Value = "Me"; 

     Type t = c1.GetType(); 
     MemberInfo[] infos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance); 

     foreach (MemberInfo info in infos) 
     { 
      if (info.MemberType.ToString().Contains("Field")) 
      { 
       Console.WriteLine("Found member {0} of type {1}", info.Name, info.MemberType); 
      } 
     } 

但我同意在使用模板化的解決方案將是更好的。

0

你想要做的是不是真的有可能,你有它的設計方式。看起來應該是這樣,但這兩個類別之間確實沒有多少關係。

我只需要添加name屬性並將其更改爲泛型類,因爲步行堆棧在所有上效率都不高,但要嘗試回答您的問題,您可以這樣做以獲得大部分所要求的內容。 ..

如果您包含字段類並且創建了實際類型的屬性,您可以通過遍歷堆棧來獲取屬性名稱(並且如果添加了許多其他人提出的使其成爲泛型類的更改,它會使它成爲一個更好的解決方案,因爲你不需要施放任何東西)。

如果你寫的客戶類是這樣的:

public class Customer 
{ 
    private Field _firstName = new Field(typeof(string), 20); 

    public string FirstName 
    { 
     get 
     { 
      return _firstName.Value as string; 
     } 
     set 
     { 
      _firstName.Value = value; 
     } 
    } 
} 

這將允許你走棧在你的領域類來獲取調用方法名(在這種情況下屬性)

string name = "<unknown>"; 
StackTrace st = new StackTrace(); 
name = st.GetFrame(1).GetMethod().Name; 

名稱現在將具有值

set_FirstName

所以你只需要剝離set_ off來獲取屬性名稱,但是如果你使用的是INotifyPropertyChanged事件,你仍然會遇到問題,因爲發件人將是Field對象,而不是客戶對象。

0

我現在確信我無法從這裏到達那裏。在我看來,如果有一個Field類型的屬性,應該很容易詢問Field的屬性名稱。很明顯,我會努力過於聰明,以避免一點點工作。我將在構造函數中添加一個屬性名稱。該字段永遠不會在一個類之外創建,因此如果屬性名稱更改,必須在兩個位置進行更改可能不方便。我並不擔心將屬性名稱改爲客戶友好型或全球化,因爲錯誤是指其他程序員試圖將字符串放在一個字段中(或將太多數字轉換爲字符串)。我喜歡泛型的建議,但還沒有制定出如何使用它,並仍然能夠將所有字段放入列表中。最終,我想按特定順序迭代列表,並構建一個類似於field1 = value1 | field2 = value2 | etc的單個字符串。