Reputation: 3004
I'm working with reflection to create some meta-tests to ensure equality between two instances of the same type. As such, I'm using a lot of vars and generics. One thing I've noticed is that with my generic functions, sometimes the Type argument is object (presumably when it can't determine the type) and other times it's the correct type.
Example:
Generic methods
public static RT[] CreateArrayWithNumItems<RT>(RT baseArgument, int numItems)
{
var a = new List<RT>();
for (int i = 0; i < numItems; i++)
a.Add((RT)DataObjectCreator.CreateUninitializedObject(typeof(RT)));
return a.Select(x => (RT)x).ToArray();
} //actual implementation more complex
private static T UnboxObject<T>(T boxedObject)
=> boxedObject;
public static object CreateUninitializedObject(Type typeObject)
=> typeObject == typeof(string) ? "" : FormatterServices.GetUninitializedObject(typeObject);
Use of methods:
var a = Model.GetType();
var unitializedObject = CreateUninitializedObject(a); //returns typed object
var objectArray = CreateArrayWithNumItems(unitializedObject, 1); //returns object array
var uob = UnboxObject(unitializedObject); //returns typed object
var uobObjectArray = CreateArrayWithNumItems(uob, 1); //returns object array
var typedArray = CreateArrayWithNumItems((dynamic) unitializedObject, 1); //returns typed object array
This seems strange to me (a.k.a I'm missing some knowledge), so I have a few questions about it.
Given the same variable, why does UnboxObject
return a typed object and CreateArrayWithNumItems
return an array of object?
Given the typed object uob
why does CreateArrayWithNumItems
return an array of object?
Lastly (and most importantly), why does casting to dynamic prior to calling CreateArrayWithNumItems
allow the generic method to determine the type?
Upvotes: 1
Views: 60
Reputation: 1064014
Without dynamic
, the compiler performs all the type evaluations based on static analysis; assuming that CreateUninitializedObject
returns object
(presumably via FormatterServices
), the call to CreateArrayWithNumItems
infers the generic type parameters based on that static type, i.e.
var objectArray = CreateArrayWithNumItems(unitializedObject, 1);
becomes
var objectArray = CreateArrayWithNumItems<object>(unitializedObject, 1);
precisely because unitializedObject
is object
.
With dynamic
, the runtime performs the work, but now it has knowledge of the actual object at runtime, so it knows that the type is (whatever Model
is). The runtime then constructs something like:
dynamic objectArray = CreateArrayWithNumItems<TheActualType>((TheActualType)unitializedObject, 1);
(how it actually does it is much more complex; it could look for non-generic methods too, for example)
So yes, using dynamic
can be a sneaky way to go from reflection (object
/Type
) code to strongly-typed generic code, but: it has a cost: dynamic
involves more work at runtime - additional reflection, and additional runtime IL emit. You also need to be aware that typedArray
is now also dynamic
(unless you cast it to something else), so everything you do with typedArray
becomes dynamic
. Increasing costs.
Upvotes: 1