Reputation: 178
Last night I learned about this wonderful operation of casting by example: a very cool way to generate a collection of some Type using a reference to an existing instance.
My problem is that although this works when you explicitly create the instance, the Type of collection produced is inaccurate if you use activator to instantiate from a Type.
class TestCollectionContent
{
public int id { get; private set; }
}
[Test]
public void TestListCastCreation()
{
var explicitCast = new TestCollectionContent (); //This casts as TestCollectionContent
var explicitList = MakeList (explicitCast); //This casts as List<CommandWithExecute>
explicitList.Add (new TestCollectionContent ());
Type clazz = typeof(TestCollectionContent);
var implicitCast = Activator.CreateInstance (clazz);//This casts as TestCollectionContent
var implicitList = MakeList (implicitCast); //This casts as List<object>
implicitList.Add (new TestCollectionContent ());
Assert.AreEqual (explicitCast.GetType (), implicitCast.GetType ()); //Succeeds
Assert.AreEqual (explicitList.GetType (), implicitList.GetType ()); //FAILS!
}
public static List<T> MakeList<T>(T itemOftype)
{
List<T> newList = new List<T>();
return newList;
}
For my purpose it is imperative that the collection be correctly cast. Any thoughts?
Note that I'm using C# with Unity3D (which uses something akin to .Net 3.5).
Upvotes: 1
Views: 71
Reputation: 56536
This code behaves this way because T
is inferred at compile time, not run time. Since implicitCast
is of type object
, it compiles with MakeList<object>
.
var implicitList = MakeList (implicitCast); // equivalent to
List<object> implicitList = MakeList<object>(implicitCast);
var explicitList = MakeList (explicitCast); // equivalent to
List<TestCollectionContent> explicitList =
MakeList<TestCollectionContent>(explicitCast);
If you want it to use the runtime type, you can use reflection or dynamic
.
Upvotes: 0
Reputation: 387677
Activator.CreateInstance
always returns an object
, so you will not get any static type information from it when using it. This will make the variable implicitCast
of type object
although its value is of a more specialized type.
Now when using generics, only the type that is available for static typing is taken into account. So when passing implicitCast
to MakeList
, all that method sees is an object
. As such, the method will be called as MakeList<object>
and will return a List<object>
, which is of course not of the same type as explicitList
.
Unfortunately (or fortunately?) you cannot really do this any better. Generics are supposed to be something for use in a static typing environment, and if you start to create types dynamically, you will lose this ability.
You could however use Activator.CreateInstance
for the list creation just as well by doing something like this:
public static IList MakeList(object itemOftype)
{
Type listType = typeof(List<>).MakeGenericType(itemOfType.GetType());
return (IList) Activator.CreateInstance(listType);
}
Of course, this will also just return an object, so you will have to cast it to a more specialized type, or use the non-generic IList
interface to have at least some access to it.
Upvotes: 1