为什么我不能在构造函数C#中分配动态数据?

时间:2015-04-02 18:10:50

标签: c# php reflection constructor

我是PHP开发人员...... 我需要做一个可以创建并填充动态方式的类,类似于PHP中的这个。

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()并获得相同的结果。

我希望能帮到我。

3 个答案:

答案 0 :(得分: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}});
}

答案 1 :(得分:1)

如果您不熟悉该语言,则应该远离使用var,这只会让事情变得复杂。

你的foreach循环中的propInfo已经是PropertyInfo,所以你不需要再次找到它:

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(或{{1} 1}}以_data开头)。

正如其他人所指出的那样,您的属性目前会导致null。尝试将它们更改为:

StackOverflowException

答案 2 :(得分: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或者替代(但这远非微不足道)使用构建动态类型带有AssemblyBuilder

TypeBuilder