Reputation: 44275
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
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
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
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