5

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
}
tetri
  • 3,753
  • 2
  • 25
  • 37
Hemerson Carlin
  • 7,354
  • 1
  • 27
  • 38
  • Couldn't you just change `MyGenericClass` to `MyGenericClass` and call `MyGenericClass.MyGenericMethod()`? (which of course would not work if you add another level of recursion...) – Markus Safar Jun 20 '14 at 18:17
  • Does `MyGenericClass` have more functionality? Wondering why the class is not simply `static` and the `T` type parameter on the method itself, like `MyGenericMethod()`? – Mike Guthrie Jun 20 '14 at 18:34
  • Why not just use the BinaryFormatter class and serialize MyClass1? http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatters.binary.binaryformatter(v=vs.110).aspx – Trevor Tubbs Jun 20 '14 at 19:30

2 Answers2

5

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);
        }
    }
}
Community
  • 1
  • 1
MarkB42
  • 725
  • 9
  • 23
  • 2
    And, if you didn't want to switch based on property name (as this would get really long and tedious) you could use a custom attribute to decorate the properties you want treated differently. – xDaevax Jun 20 '14 at 18:47
  • 1
    Depending on requirements you wouldn't even need to decorate the properties. You could just have a way to test for [simple vs complex types](https://gist.github.com/jonathanconway/3330614) – MarkB42 Jun 20 '14 at 18:52
  • Nice link, that is a helpful little extension. – xDaevax Jun 20 '14 at 19:23
1

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".

xDaevax
  • 2,012
  • 2
  • 25
  • 36