问题
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