CodingWithSpike
CodingWithSpike

Reputation: 43718

.NET Generics: Using an Activator created type as a generic shows wrong Type? Need workaround

This really has my stumped today. I'm sure its simple, but... Here is my sample code:

using System;
using System.Collections;

namespace ConsoleApplication1
{
    class Program
    {
        public ArrayList SomeProp { get; set; }

        static void Main(string[] args)
        {
            // Get the Type of a property by reflection.
            Type myPropType = typeof(Program).GetProperty("SomeProp").PropertyType;

            // Create an instance of the determined Type.
            var x = Activator.CreateInstance(myPropType);

            // Now try to use that instance, passing to a method that takes a generic.
            WhatAmI(x);

            Console.ReadKey();
        }

        private static void WhatAmI<T>(T x)
        {
            Console.WriteLine("T is: " + typeof(T).FullName);
            Console.WriteLine("x is: " + x.GetType().FullName);
        }
    }
}

The output here is:

T is: System.Object
x is: System.Collections.ArrayList

Basically what is going on here is I have some property on some class. It doesnt really matter what/where that comes from. The important part is that I get the PropertyInfo for that property. From there I create a new instance of its Type with Activator.

Now If I pass that created instance to a function that takes a generic parameter, the generic Type always comes across as Object, because Activator.CreateInstance() returns an Object, so "var x" is an Object, not, in this case, an ArrayList.

What I need to happen is for the generic type to be the actual type, int his case "ArrayList".

I know there is an Activator.CreateInstance() that I can use instead, but I can't use a Type (PropertyInfo.PropertyType) within the angle brackets for that method.

I could also just cast the returned object, like:

myPropType x = (myPropType)Activator.CreateInstance(myPropType);

But obviously that doesn't compile... Plus it wouldn't be valid anyway because the cast is compile time, and I don't know the type until runtime, but conceptually its what I need...

So I'm stuck with this Type, but I can't figure out how to get it passed over to the WhatAmI() method, and have T be ArrayList, not Object.

Ideas?

Upvotes: 1

Views: 1679

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062770

To call generic methods using a Type at runtime, you need to use reflection - and MakeGenericMethod in particular, something like:

typeof(Program).GetMethod("WhatAmI",
    BindingFlags.Static | BindingFlags.NonPublic)
    .MakeGenericMethod(x.GetType()).Invoke(null, new object[] { x });

Otherwise, the compiler infers the <T> from the variable type - object in this case.

One side point here is that you can improve things by only doing the reflection once - i.e. don't use Activator, but use a generic constraint instead:

    private static void WhatAmI<T>() where T : new()
    {
        T x = new T();
        Console.WriteLine("T is: " + typeof(T).FullName);
        Console.WriteLine("x is: " + x.GetType().FullName);
    }

Defines the method without an arg, but with a default constructor; then:

    // Get the Type of a property by reflection.
    Type myPropType = typeof(Program).GetProperty("SomeProp").PropertyType;

    // Now call a generic method just using the type
    typeof(Program).GetMethod("WhatAmI",
        BindingFlags.Static | BindingFlags.NonPublic)
            .MakeGenericMethod(myPropType).Invoke(null, null);

calls the method just using the type - the instance is created inside the method. This is usually faster than repeated reflection.

Upvotes: 7

Related Questions