P.Brian.Mackey
P.Brian.Mackey

Reputation: 44275

What is the utility of allowing unbound types to be referenced in the typeof expression?

Given

        int foo = 1;
        Type unboundType = typeof(List<>);
        Type w = unboundType.MakeGenericType(typeof(int));

        if (w == typeof(List<int>))
        {
            Console.WriteLine("Yes its a List<int>");

            try
            {
                ((List<int>)(object)w).Add(foo);
            }
            catch(InvalidCastException)
            {
                Console.WriteLine("No you can't cast Type");
            }
        }   

I can verify that the type indeed matches a constructed type and perform an action based on said constructed type. However, I cannot cast Type to it's class using as or an explicit cast. Is there a practical purpose for allowing developers to create a Type of unbound type or does this functionality exist solely to support the language in some way?

Upvotes: 2

Views: 205

Answers (3)

xxbbcc
xxbbcc

Reputation: 17327

Type is not a placeholder for a given type - it's a specific type itself to describe other types. There's no point in trying cast it to a different, unrelated type because metadata (obviously) cannot act for that specific type.

The metadata types are used to inspect the various aspects of specific types, not to create one. If you want to create instances of types in a generic way, you can use the Activator class for that.

Type oType = ...; // get a Type instance here about a type
object[] oArgs = { oParam1, oParam2 }; // constructor parameters (if any)

return ( Activator.CreateInstance ( oType, oArgs ) );

This gives you the ability to create types based on strings, for example. You can get a Type instance for System.String (or from a function call like GetTypeNameFromUser(...)) and then create an instance of that type. (Activator has direct support for just taking a string but internally, it uses Type to look up the type that needs to be instantiated.)

Since all types are equal, you can create a Type instance for an unbound generic type just like any other type - at the very least, it allows you to inspect its properties and methods. (As the accepted answer shows, you can also use the Type instance to create specialized generic types using MakeGenericType.)

Upvotes: 0

Marc Gravell
Marc Gravell

Reputation: 1062745

Not everything can be done at compile time. Sometimes, particularly in library code, you need to take what you are given. In scenarios where you are given just an object or a Type and need to do some clever processing, unbound types can be really helpful; for example:

object obj = NoIdeaWhatThisReturns();
IList list = (IList)Activator.CreateInstance(
                 typeof(List<>).MakeGenericType(obj.GetType()));
list.Add(obj);

Basically; scenarios that use a lot of reflection or meta-programming will probably find themselves using unbound types at some point.

Upvotes: 4

Chris Sinclair
Chris Sinclair

Reputation: 23208

In the code you posted, you didn't actually instantiate an object of that type anywhere. You were simply trying to cast an instance of System.Type to List<int> which doesn't make sense. If you update your code to actually create an instance, it works:

int foo = 1;
Type unboundType = typeof(List<>);
Type w = unboundType.MakeGenericType(typeof(int));

if (w == typeof(List<int>))
{
    Console.WriteLine("Yes its a List<int>");
    object obj = Activator.CreateInstance(w);

    try
    {
        ((List<int>)obj).Add(foo);
        Console.WriteLine("Success!");
    }
    catch(InvalidCastException)
    {
        Console.WriteLine("No you can't cast Type");
    }
}

Maybe I'm just missing the crux of your question. Certainly depending on your logic, you could have if/else checks based on some type you don't know at compile time (in your example, you know you're working with int, but perhaps at runtime that could be other types as desired)

EDIT: Just to provide an example of a truly runtime usage, consider the following:

public object CreateList(Type elementType, object initialValue)
{
    if (!elementType.IsAssignableFrom(initialValue.GetType()))
        throw new ArgumentException("Incompatible types!");

    Type unboundType = typeof(List<>);
    Type listType = unboundType.MakeGenericType(elementType);
    object list = Activator.CreateInstance(listType);
    var addMethod = listType.GetMethod("Add");
    addMethod.Invoke(list, new []{initialValue});
    return list;
}

This lets us create a List<T> out of some unknown type/object at runtime. Some usage:

object intList = CreateList(typeof(int), 1);
object stringList = CreateList(typeof(string), "asdf");

object objectFromSomewhere = GetMyUnknownObject();
object someUnknownListType = CreateList(objectFromSomewhere.GetType(), objectFromSomewhere);

So, you might not be able to do much with the objects as are; probably could have treated them as IEnumerable at least. But that's up to what your system needs to do.

EDIT: Forgot about the IList interface:

public IList CreateList(Type elementType, object initialValue)
{
    if (!elementType.IsAssignableFrom(initialValue.GetType()))
        throw new ArgumentException("Incompatible types!");

    Type unboundType = typeof(List<>);
    Type listType = unboundType.MakeGenericType(elementType);
    IList list = (IList)Activator.CreateInstance(listType);
    list.Add(initialValue);
    return list;
}

Upvotes: 4

Related Questions