2015-04-02 99 views
-1

我是一名PHP開發人員... 我需要創建一個可以創建並填充動態方式的類,類似於PHP。爲什麼我不能在構造函數C#中分配動態數據?

class Person{ 
    private $name; 
    private $age; 

    function __construct($params = array()){ 
     foreach ($this as $key => $val) { 
      $this -> $key = (isset($params[$key])) ? $params[$key] : ""; 
     } 
    } 

    function getName(){ 
     return $this->name; 
    } 

    function getAge(){ 
     return $this->age; 
    } 

    function setName($value){ 
     $this->name = $value; 
    } 

    function setAge($value){ 
     $this->age = $value; 
    } 
} 

我讀了關於C#中的反射,但我沒有找到正確的方法。 這是我的C#代碼

public class Person 
{ 
    private String _name { get { return _name; } set { _name = value; } } 
    private int _age { get { return _age; } set { _age = value; } } 
    public Person() 
    { 

    } 

    public Person(Hashtable _data) 
    { 
     PropertyInfo[] propertyInfos; 
     propertyInfos = typeof(Person).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance); 
     foreach (var propInfo in propertyInfos) 
     { 
      typeof(Person).GetProperty(propInfo.Name).SetValue(this, _data[propInfo.Name]); 
     } 
    } 
} 

在運行時我得到一個異常

對象引用不設置到對象的實例。

typeof(Person)我試圖將其更改爲this.getType(),並且我得到了相同的結果。

我希望能幫到我。

+7

你是屬性setter自稱...永遠。你不想這樣定義你的屬性。你也應該使用'Dictionary'而不是'HashTable'。當.NET 2.0推出時,'HashTable'變得過時。 – Servy 2015-04-02 18:11:55

+0

更好地使用'IDictionary '。 'Hashtable'是舊的.NET 1.0的東西。另外,對於私人成員,您可以使用字段而不是屬性。 – 2015-04-02 18:14:42

+0

也有沒有得到一個屬性信息的要點,然後問具有該屬性信息的名稱的屬性類型以獲取相同的屬性信息。您已經擁有屬性信息,只需使用它。 – Servy 2015-04-02 18:16:08

回答

3

您正在抓取對象上的所有屬性,然後在散列表中查找它們。你可能想要相反 - 散列表中的所有對象都設置爲對象的屬性。否則,當你不指定每個成員時,你會得到一個例外。

正如Alexei指出的那樣,NullReferenceException是由於第二次調用GetProperties只在沒有提供BindingFlags時返回公共屬性。由於沒有公共財產,你會得到一個例外。

因爲C#是強類型的,所以會遇到很多PHP中沒有的問題。這些包括使用不匹配或轉換爲屬性類型的類型的對象設置值,數據參數中不作爲屬性存在的條目等。我已盡我所能記錄我看到的陷阱下面。

這裏是Person類將是什麼樣子(我已經清理了一些風格和使用的類,使其感覺更像是一個C#類):

public class Person 
{ 
    private string name { get; set; } 
    private int age { get; set; } 

    public Person() 
    { 

    } 

    public Person(IDictionary<string,object> data) 
    { 
     foreach (var value in data) 
     { 
      // The following line will be case sensitive. Do you need to standardize the case of the input dictionary before getting the property? 
      PropertyInfo property = typeof(Person).GetProperty(value.Key, BindingFlags.Instance | BindingFlags.NonPublic); 
      if (property != null) 
      { 
       property.SetValue(this, value.Value); // You are allowing any old object to be set here, so be prepared for conversion and casting exceptions 
      } 
      else 
      { 
       // How do you want to handle entries that don't map to properties? Ignore? 
      } 
     } 
    } 
} 

這裏是一個例子用法:

static void Main(string[] args) 
{ 
    var person = new Person(new Dictionary<string,object>() {{"name" ,"Mike"}, {"age", 32}}); 
} 
+0

代碼是正確的,但我相信NRE的解釋是向後:)(NRE實際上是因爲'GetProperty'只返回公共屬性,如果沒有指定綁定標誌)。 – 2015-04-02 19:56:11

+0

謝謝你,你的代碼對我來說非常有用。我將實現兩個重載,一個是Dictionary,另一個是Hasstables。 你的代碼是更好的方法。 – 2015-04-02 19:59:48

+0

謝謝Alexei。我已經糾正了答案。 – mikdav 2015-04-02 20:13:08

1

如果您不熟悉該語言,則應該遠離使用var,這隻會使事情變得複雜。

的在你的foreach循環已經propInfoPropertyInfo,所以你不需要找了一遍:

BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance; 
PropertyInfo[] propertyInfos = typeof(Person).GetProperties(flags); 
foreach (PropertyInfo propInfo in propertyInfos) 
{ 
    propInfo.SetValue(this, _data[propInfo.Name]); 
} 

NullReferenceException可能是由你的原始代碼以下部分引起的:

typeof(Person).GetProperty(propInfo.Name)... 

由於沒有BindingFlags提供給GetProperty()這個時候,它看起來對公共實例屬性,而當沒有找到這樣的屬性,它返回null(即,或_datanull開頭)。

正如其他人已指出,您的屬性目前將導致StackOverflowException s。嘗試將它們更改爲:

private String _name { get; set; } 
private int _age { get; set; } 
+0

謝謝你的代碼對我來說非常有用。 – 2015-04-02 19:56:38

1

我在想你爲什麼要這樣做。可能有更好,更習慣的C#設計來實現你想要的行爲。但是我們無法知道,因爲問題中沒有提到額外的上下文信息。

所以我會簡單地試着回答你的問題。下面的版本將您的代碼,使用自動屬性和一個簡單的字典查找從提供的字典中初始化其成員。還請注意,這並不需要任何反思,因爲關於此類的成員沒有任何dynamic

public class Person 
{ 
    public string Name { get; set; } 
    public int Age { get; set; } 

    public Person(IDictionary<string, object> data) 
    { 
     // What to do if the map does not contain "Name" or "Age" ? 
     // Right now: initialize to default value. 
     Name = TryLookup<string>(data, "Name", null); 
     Age = TryLookup<int>(data, "Age", default(int)); 
     // What to do if the map contains other items that do not 
     // map to a member variable? 
    } 

    private static T TryLookup<T>(IDictionary<string, object> data, string key, T defaultValue) 
    { 
     return data.ContainsKey(key) ? (T)data[key] : defaultValue; 
    } 
} 

如果你實際上真的非常需要一個動態類型,而不是靜態定義的類型與固定構件的屬性,可以使用一個ExpandoObject或者可選地(但這遠遠微不足道)使用建立動態型一個AssemblyBuilderTypeBuilder

+0

非常感謝你不要以爲我需要一個動態類型是我在PHP中逃避 – 2015-04-02 18:56:32

相關問題