Reflection can't convert object

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

Answers (3)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

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

Alexei Levenkov
Alexei Levenkov

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

Klaus G&#252;tter
Klaus G&#252;tter

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

Related Questions