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

Why I can't assign dynamic data in constructor C#?

我是 PHP 开发人员... 我需要做一个可以动态创建和填充的class,类似于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]);
        }
    }
}

在运行时我得到一个异常

Object reference not set to an instance of an object.

typeof(Person) 我尝试将其更改为 this.getType() 并且我得到了相同的结果。

希望对我有帮助。

如果您是该语言的新手,您应该远离使用 var,它只会让事情变得复杂。

你的foreach-loop中的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(),它寻找public实例属性,当没有找到这样的属性时,它returns null(或者 _data 开头是 null)。

正如其他人所指出的,您的属性当前将导致 WhosebugExceptions。尝试将它们更改为:

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

您正在获取对象的所有属性,然后在哈希表中查找它们。您可能希望相反——哈希表中的所有对象都设置为对象的属性。否则,当您没有指定每个成员时,您将得到一个例外。

正如 Alexei 指出的那样,NullReferenceException 是由于第二次调用 GetProperties 在未提供 BindingFlags 时仅返回 public 属性。由于没有 public 属性,您会遇到异常。

因为 C# 是强类型的,所以您 运行 遇到了许多 PHP 中没有的问题。这些包括使用不匹配或转换为 属性 类型的类型的对象设置值,数据参数中不作为属性存在的条目等。我已尽力记录我在下面看到的问题。

下面是人物 class 的样子(我清理了一些样式并使用了 classes 使其更像 C# class) :

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}});
}

我想知道你为什么要这样做。可能有更好、更惯用的 C# 设计来实现您想要的行为。但是我们不知道,因为问题中没有提到额外的上下文信息。

所以我会简单地尝试回答你的问题。下面的版本使用您的代码,使用自动属性和一个简单的字典查找来从提供的字典中初始化其成员。另请注意,这不需要任何反射,因为没有关于此 class.

成员的任何内容 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 or alternatively (but this is far from trivial) build a dynamic type using an AssemblyBuilder with a TypeBuilder