使用反射在构造函数中初始化(列表)属性

时间:2018-01-19 14:42:21

标签: c# reflection constructor properties initialization-list

我正在尝试使用反射初始化类(列表)中的所有属性:

public class EntitiesContainer
{
    public IEnumerable<Address> Addresses { get; set; }
    public IEnumerable<Person> People { get; set; }
    public IEnumerable<Contract> Contracts { get; set; }

    public EntitiesContainer()
    {
        var propertyInfo = this.GetType().GetProperties();
        foreach (var property in propertyInfo)
        {
            property.SetValue(property, Activator.CreateInstance(property.GetType()), null);
        }
    }
}

我得到例外:

  

没有参数,没有为此对象定义构造函数。

我很感激提示。

4 个答案:

答案 0 :(得分:3)

一般来说,您要创建的对象类型为property.PropertyType;并且您要设置值的对象是this,所以:

property.SetValue(this, Activator.CreateInstance(property.PropertyType), null);

但是!您的属性为IEnumerable<T>,而不是List<T> - 无法创建界面,只能创建具体类型。因此,您需要做很多工作,将通用IEnumerable<Foo>解构为Foovar args = type.GetGenericTypeArguments())并构建List<Foo>typeof(List<>).MakeGenericType(args)) 。或者只是将属性类型更改为List<T>

坦率地说,这样做会更容易:

public IEnumerable<Address> Addresses { get; set; } = new List<Address>();
public IEnumerable<Person> People { get; set; } = new List<Person>();
public IEnumerable<Contract> Contracts { get; set; } = new List<Contract>();

或:

public List<Address> Addresses { get; } = new List<Address>();
public List<Person> People { get; } = new List<Person>();
public List<Contract> Contracts { get; } = new List<Contract>();

答案 1 :(得分:3)

如果您将属性定义为具体类型,则可以执行此操作。这实际上有效:

public class EntitiesContainer
{
    public List<Address> Addresses { get; set; }
    public List<Person> People { get; set; }
    public List<Contract> Contracts { get; set; }

    public EntitiesContainer()
    {
        var propertyInfo = this.GetType().GetProperties();
        foreach (var property in propertyInfo)
        {
            property.SetValue(this, Activator.CreateInstance(property.PropertyType));
        }
    }
}

您无法创建IEnumerable<T>的实例,因为它是一个界面。

但你为什么要这个呢?您最好使用C#6中引入的自动属性初始化程序初始化属性:

public class EntitiesContainer
{
    public IEnumerable<Address> Addresses { get; set; } = new List<Address>;
    public IEnumerable<Person> People { get; set; } = new List<Address>;
    public IEnumerable<Contract> Contracts { get; set; } = new List<Address>;
}

答案 2 :(得分:1)

总结我想要实现的是在下面的构造函数中调用的方法:

    private void InitializeAllCollections()
    {
        var properties = this.GetType().GetProperties();

        foreach (var property in properties)
        {
            var genericType = property.PropertyType.GetGenericArguments();
            var creatingCollectionType = typeof(List<>).MakeGenericType(genericType);
            property.SetValue(this, Activator.CreateInstance(creatingCollectionType));
        }
    }

谢谢你们的帮助。 :)

答案 3 :(得分:0)

我也有类似的需求:在为单元测试创​​建业务对象时,我想将所有未初始化的列表默认为新列表,这样,如果测试需要向列表中添加一些内容,则不必担心初始化它在那里。和OP一样,我有太多业务对象无法将它们全部更改为默认对象。我的解决方案是其他解决方案的混合体。例外是我只需要List属性,并且仅当它们尚未初始化时:

    public static T DefaultLists<T>(this T obj)
    {
        var properties = obj.GetType().GetProperties().Where(q => q.PropertyType.Name == "List`1" && q.GetValue(obj) == null);
        foreach(var property in properties)
            property.SetValue(obj, Activator.CreateInstance(property.PropertyType));

        return obj;
    }

现在我的示例对象创建者可以返回新的businessObject.DefaultLists();