Reputation: 21
I want use reflection to dynamic to populate any class, but when this class has a property as other class I have encountered problem. in this example above I reproduce, a part of code to show same dificult. I can't convert object to address.
Can you help me?
public class Destinatary
{
public string DetinataryID { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string Place { get; set; }
public string PostalCode { get; set; }
}
class Program
{
static void Main(string[] args)
{
Destinatary destinataryTest = new Destinatary();
Type type = typeof(Destinatary);
destinataryTest = GenerateDataSample(destinataryTest);
Console.WriteLine($"Name: {destinataryTest.Name}");
Console.WriteLine($"Place: {destinataryTest.Address.PostalCode}");
Console.WriteLine($"City: {destinataryTest.Address.City.Name}");
Console.ReadLine();
}
private static T GenerateDataSample<T>(T classToWork)
{
Type typeToWork = typeof(T);
object tipoInstance = Activator.CreateInstance(typeToWork);
foreach (var classProperty in typeToWork.GetProperties())
{
if (classProperty.PropertyType.FullName.StartsWith("System."))
{
var propertyVal = RandomString(10,false );
classProperty.SetValue(tipoInstance, propertyVal, null);
}
else
{
var instanceIntermediate = Activator.CreateInstance(classProperty.PropertyType);
instanceIntermediate = GenerateDataSample(instanceIntermediate);
classProperty.SetValue(tipoInstance, instanceIntermediate, null); //here there is a probleman (Cant convert Object to Address)
}
}
return (T)tipoInstance;
}
}
Upvotes: 0
Views: 577
Reputation: 112269
Why do you want to convert the value? The object is already of type Address
and the set method is declared as
public void SetValue (object obj, object value);
i.e., it accepts a variable of type object
.
The problem occurs when calling GenerateDataSample<T>()
, because T
is determined at compile time and the compile time type of instanceIntermediate
is object
, therefore GenerateDataSample
will always try to create an object of type System.Object
for properties. Don't make GenerateDataSample
generic.
Also, you don't actually need to pass an instance but only a type to GenerateDataSample
.
private static object GenerateDataSample(Type typeToWork)
{
object tipoInstance = Activator.CreateInstance(typeToWork);
foreach (PropertyInfo classProperty in typeToWork.GetProperties()) {
object propertyVal;
if (classProperty.PropertyType == typeof(string)) {
propertyVal = RandomString(10, false);
} else {
propertyVal = GenerateDataSample(classProperty.PropertyType);
}
classProperty.SetValue(tipoInstance, propertyVal);
}
return tipoInstance;
}
You can also create an overloaded generic version:
private static T GenerateDataSample<T>()
{
return (T)GenerateDataSample(typeof(T));
}
Then you can create sample data with:
Destinatary destinataryTest = GenerateDataSample<Destinatary>();
Upvotes: 1
Reputation: 100527
var instanceIntermediate = Activator.CreateInstance(...
- on this line compile time type of instanceIntermediate
is object
. So when you call GenerateDataSample(instanceIntermediate)
it is calling GenerateDataSample<object>(instanceIntermediate)
and not GenerateDataSample<Address>(instanceIntermediate)
as you probably expect.
Fix: since GenerateDataSample
does not actually care about input parameter and only type - change it from generic to regular method taking type - object GenerateDataSample(Type typeToWork)
Upvotes: 0
Reputation: 11977
Generics are resolved at compile time, not at run time.
In this line
instanceIntermediate = GenerateDataSample(instanceIntermediate);
instanceIntermediate is an Address object, but the compiler here only knows that it is an object. So it calls GenerateDataSample which will construct an object in this line
object tipoInstance = Activator.CreateInstance(typeToWork);
To have GenerateDataSample create an instance of the type of the object classToWork, use
object tipoInstance = Activator.CreateInstance(classToWork.GetType());
Upvotes: 1