Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to trigger a Generic Class method recursively changing the type of T?

I've created a Generic Class to parse some data into another instance of a class (MyClass1). Since MyClass1 has only built-in C# types, my GenericMethod works fine. The problem starts to grow when MyClass1 has another MyClass2 property and I still want to invoke my GenericMethod to parse my data.

I can't trigger my Generic Class method inside its scope since I need to change the type of T. Is there any way to solve this problem?

public class MyClass1 
{
    public int MyIntProperty { get; set; }
    public string MyStringProperty { get; set; }

    public MyClass2 MyClass2Property { get; set; }
}

public class MyClass2 
{
    public int MyOtherIntProperty { get; set; }
    public string MyOtherStringProperty { get; set; }
    public bool MyOtherBoolProperty { get; set; }
}

public class MyGenericClass<T> where T : class
{
    public static T MyGenericMethod()
    {
        T o = (T)Activator.CreateInstance(typeof(T));
        PropertyInfo[] pi = typeof(T).GetProperties();

        for(int i = 0; i < pi.Count(); i++) 
        {
            if(pi[i].Name == "MyClass2Property") 
            {
                //How to proceed ?
                MyGenericClass<???>.MyGenericMethod(); 
            }
            else 
            {
                pi[i].SetValue(o, Convert.ChangeType(someValue, pi[i].PropertyType), null);
            }
        }
    }
}        

public static void Main(string[] args) 
{
    MyClass1 mc1 = MyGenericClass<MyClass1>.MyGenericMethod();
    //Do something with mc1
}
like image 643
mersocarlin Avatar asked Jun 20 '14 17:06

mersocarlin


2 Answers

You can look at this post

and maybe try something like this

public static class MyGenericClass<T> where T : class
{
    public static T MyGenericMethod()
    {
    T o = (T)Activator.CreateInstance(typeof(T));
    PropertyInfo[] pi = typeof(T).GetProperties();

    for(int i = 0; i < pi.Count(); i++) 
    {
        if(pi[i].Name == "MyClass2Property") 
        {
            //How to proceed ?
            Type t = typeof (MyGenericClass<>);
            Type genericType = t.MakeGenericType(new System.Type[] { pi[i].PropertyType });
            var c = Activator.CreateInstance(genericType);
            dynamic mgm = Convert.ChangeType(c, genericType);
            mgm.MyGenericMethod(); 
        }
        else 
        {
            pi[i].SetValue(o, Convert.ChangeType(someValue, pi[i].PropertyType), null);
        }
    }
}
like image 114
MarkB42 Avatar answered Sep 28 '22 14:09

MarkB42


Depending on your needs, you could also define some additional meta information about the property indicating what you would like to do with it if found.

Building on others' comments and answers, here is what I came up with including an attribute decoration that allows you to dynamically build objects and has the following enhancements:

  • Properties can be named anything you want
  • No need for if statements as new properties are added.
  • No need for the MyGenericMethod method to ever change.
  • Additional meta-information can be added to the custom attribute to add further customization in the future.
  • Objects can be nested as deeply as needed.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Dynamic;

public class MyClass1 {

    public int MyIntProperty { get; set; }
    public string MyStringProperty { get; set; }

    [MyCustom()]
    public MyClass2 MyClass2Property { get; set; }
}

public class MyClass2 {

    public int MyOtherIntProperty { get; set; }
    public string MyOtherStringProperty { get { return "oooh, fancy"; } set {} }
    public bool MyOtherBoolProperty { get; set; }

}

public static class MyGenericClass<T> where T : class {

    public static T MyGenericMethod() {
        T o = (T)Activator.CreateInstance(typeof(T));
        PropertyInfo[] pi = typeof(T).GetProperties();

        for (int i = 0; i < pi.Count(); i++) {
            if (pi[i].GetCustomAttributes(true).Any() && pi[i].GetCustomAttributes(true).Where((x) => x is MyCustomAttribute).Any()) {
                //How to proceed ?
                var c = Activator.CreateInstance(pi[i].PropertyType);
                Type t = typeof(MyGenericClass<>);
                Type genericType = t.MakeGenericType(new System.Type[] { pi[i].PropertyType });
                MethodInfo m = genericType.GetMethod(MethodInfo.GetCurrentMethod().Name);
                c = m.Invoke(null, null);
                pi[i].SetValue(o, c, null);
            } else {
                //Normal property assignment.
            }
        }
        return o;
    }
}

public class Program {

    public static void Main(string[] args) {
        MyClass1 mc1 = MyGenericClass<MyClass1>.MyGenericMethod();
        //Do something with mc1
        Console.WriteLine(mc1.MyClass2Property.MyOtherStringProperty);
        Console.ReadLine();
    }

}

[AttributeUsage(AttributeTargets.Property, AllowMultiple=false)]
public class MyCustomAttribute : Attribute {

}

I tweaked this so it can be run as is.

Edit:

I also changed the code to invoke the method being called on itself to avoid a "magic string".

like image 27
xDaevax Avatar answered Sep 28 '22 16:09

xDaevax