Initializing (list) properties in constructor using reflection

谁说胖子不能爱 提交于 2020-04-14 03:37:13

问题


I am trying to initialize all properties in class (lists) with using reflection:

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

I am getting exception:

No constructor has been defined for this object without parameters.

I would appreciate tips.


回答1:


You can do this provided that you define the properties as concrete types. This actually works:

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

You cannot create an instance of an IEnumerable<T> because it's an interface.

But why would you want to to this? You'd better initialize the properties using the auto-property initializer that was introduced in 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:


In general here, the type of object you want to create is property.PropertyType; and the object upon which you want to set the value is this, so:

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

But! your properties are IEnumerable<T>, not List<T> - can't create an interface, only a concrete type. So you'd have to do a lot of work with deconstructing the generic IEnumerable<Foo> to Foo (var args = type.GetGenericTypeArguments()) and constructing a List<Foo> (typeof(List<>).MakeGenericType(args)). Or just change the property types to List<T>!

Frankly, it would be easier to just do:

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

or:

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



回答3:


To sum up what I wanted to acheive was method called in constructor like below:

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

Thanks guys for your help. :)




回答4:


I had a similar need: when creating business objects for unit tests, I want to default all uninitialized Lists to new Lists, so that if a test needs to add something to a list, I don't have to worry about initializing it there. And like the OP, I have too many business objects to change them all to default. My solution is a mix of the others; the exceptions being I only want List properties, and only if they are not yet initialized:

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

Now my sample object creator can return new businessObject.DefaultLists();



来源:https://stackoverflow.com/questions/48343551/initializing-list-properties-in-constructor-using-reflection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!