2

I have the following code:

public static List<object[]> Serialise2D_Rec<T>(IEnumerable<T> data)
{
    int numElts = 0;
    foreach (var item in data)
        numElts++;

    Type t = typeof(T); // Get type pointer
    PropertyInfo[] propList = t.GetProperties();


    List<object[]> toret = new List<object[]>();

    for (long propID = 0; propID < propList.Count(); ++propID)
    {
        var proptype = propList[propID].PropertyType;
        if (proptype.IsPrimitive || proptype == typeof(Decimal) || proptype == typeof(String))
        {
            toret.Add(new object[numElts + 1]);
            toret[toret.Count - 1][0] = propList[propID].Name;
            int row = 1;
            foreach (T item in data)
            {
                toret[toret.Count - 1][row] = propList[propID].GetValue(item, null);
                row++;
            }
        }
        else
        {
            var lst = (IList)Activator.CreateInstance((typeof(List<>).MakeGenericType(proptype)));
            foreach (T item in data)
            {
                lst.Add(propList[propID].GetValue(item, null));
            }
            List<object[]> serialisedProp = Serialise2D_Rec(lst);

        }
    }
    return toret;

}

However this line will fail with:

List<object[]> serialisedProp = Serialise2D_Rec(lst);

With the error:

****: error CS0411: The type arguments for method '****.****.Serialise2D_Rec<T>(System.Collections.Generic.IEnumerable<T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

How can I specify the type in the recursion, it seems that the syntax for dynamic generic types is not that trivial.

Kevin Brechbühl
  • 4,717
  • 3
  • 24
  • 47
BlueTrin
  • 9,610
  • 12
  • 49
  • 78

5 Answers5

4

I don't see a valid use case for generics in your case. Usage of generics assume you are able to statically identify the type on compile-time (or its ancestor, at least). Why don't you just accept the non-generic IEnumerable? If you do need to supply some base type of items in data, then supply it as a parameter:

public static List<object[]> Serialise2D_Rec(IEnumerable data, Type t)
{
    …
    for (…)
    {
        if (…)
        {
        }
        else
        {
            …
            List<object[]> serialisedProp = Serialise2D_Rec(lst, proptype);
        }
    }
}

Side note: use the extension method data.Count() instead of foreach (var item in data) numElts++;.

Ondrej Tucny
  • 27,626
  • 6
  • 70
  • 90
  • Duly noted, however I am still interesting in knowing how I can create the call to the method dynamically. BTW in this case I need T to get the headers correctly in the case the IEnumerable does not contain an element. – BlueTrin Jan 27 '14 at 17:48
  • 1
    You'd have to retrieve `MethodInfo` for `Serialize2D_Rec` and the use [`MethodInfo.MakeGenericMethod`](http://msdn.microsoft.com/en-us/library/system.reflection.methodinfo.makegenericmethod.aspx) to build an instance matching `proptype`. However, I doubt it's worth the clutter that will arise in your code. – Ondrej Tucny Jan 27 '14 at 18:05
2

Since your type is dynamic, you won't know the type of the generic parameter until run-time. For this reason, you must also treat the function as dynamic, because you won't know the "type" of the function's generic signature until run-time.

You must use reflection to call a generic function dynamically. See How do I use reflection to call a generic method?

Community
  • 1
  • 1
JDB
  • 25,172
  • 5
  • 72
  • 123
  • Thanks, I ended up using this one. – BlueTrin Jan 27 '14 at 18:07
  • 1
    @BlueTrin - Make sure to read the [second answer](http://stackoverflow.com/a/5134251/211627) on that question. Might be worth using for the sake of code maintainability. – JDB Jan 27 '14 at 19:05
  • This is really good stuff, I will use it when I get back to the office. In my case I know the most common types that will be called, so I could add this for the sake of having the check. – BlueTrin Jan 27 '14 at 21:31
1

Try this:

// Get The Method by reflection
    MethodInfo serializerInfo = typeof(Serializer).GetMethod("Serialise2D_Rec",
                        BindingFlags.Public | BindingFlags.Static); 

//Make a Generic instance of the type you want
    serializerInfo = serializerInfo.MakeGenericMethod(lst.GetType());

    List<object[]> serialisedProp = serializerInfo.Invoke(null, new object[] {lst});

Instead of Serializer in the typeof() put the class that hold your func Serialise2D_Rec

qwark
  • 493
  • 1
  • 4
  • 15
1

It doesn't seem like you can use generics in this case. The line fails because lst is a non-generic IList and it's passed as arguent to Serialise2D_Rec, which requires an IEnumerable<T>.

I suggest that you change your method not to be generic; it uses reflection anyway, it may not have a big impact.

Try this:

public static List<object[]> Serialise2D_Rec(IList data)
{
    int numElts = 0;
    foreach (var item in data)
        numElts++;

    if (data.Count == 0)
        throw new Exception("Cannot handle empty lists.");

    Type t = data[0].GetType(); // Get type pointer
    PropertyInfo[] propList = t.GetProperties();

    List<object[]> toret = new List<object[]>();

    for (long propID = 0; propID < propList.Count(); ++propID)
    {
        var proptype = propList[propID].PropertyType;
        if (proptype.IsPrimitive || proptype == typeof(Decimal) || proptype == typeof(String))
        {
            toret.Add(new object[numElts + 1]);
            toret[toret.Count - 1][0] = propList[propID].Name;
            int row = 1;
            foreach (object item in data)
            {
                toret[toret.Count - 1][row] = propList[propID].GetValue(item, null);
                row++;
            }
        }
        else
        {
            var lst = (IList)Activator.CreateInstance((typeof(List<>).MakeGenericType(proptype)));
            foreach (object item in data)
            {
                lst.Add(propList[propID].GetValue(item, null));
            }
            List<object[]> serialisedProp = Serialise2D_Rec(lst);

        }
    }

    return toret;
}
Doug
  • 6,322
  • 3
  • 29
  • 48
  • I voted up your answer, but in my case I need it to work even on empty IEnumerable, because it prints the name of the property in the first element of the Object[]: it is used later because the user is an Excel addin, so we print the headers of the name of the properties of the object even if there was no item returned. – BlueTrin Jan 27 '14 at 21:34
0

Yes, that's because lst is of type object.

You'll need to dynamically invoke the correct generic type of Serialise2D_Rec<T>()

Jon G
  • 4,083
  • 22
  • 27