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.

3
  • What type is the parameter lst? Commented Jan 27, 2014 at 17:44
  • @HåkanFahlstedt: It's coming from near the end as part of the recursion; it's compile-time type is IList (non-generic) Commented Jan 27, 2014 at 17:47
  • Ok, as you can see in the answers there is a problem with using a non-generic type here. Commented Jan 27, 2014 at 17:53

5 Answers 5

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++;.

Sign up to request clarification or add additional context in comments.

2 Comments

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.
You'd have to retrieve MethodInfo for Serialize2D_Rec and the use MethodInfo.MakeGenericMethod to build an instance matching proptype. However, I doubt it's worth the clutter that will arise in your code.
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?

3 Comments

Thanks, I ended up using this one.
@BlueTrin - Make sure to read the second answer on that question. Might be worth using for the sake of code maintainability.
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.
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

Comments

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;
}

1 Comment

I voted up your answer, but in my case I need it to work even on empty IEnumerable<T>, 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.
0

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

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

1 Comment

Well how do I build the correct type using Invoke and MakeGenericMethod ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.