It was just another day with .NET. Until I had to get generic method of a static class with a generic parameter, using reflection for serialization. Doesn't sound so bad. GetRuntimeMethod("x", new[] { type })
, as usual should do the trick, or so I thought.
Now, this method keeps returning null for the following variant: public static Surrogate<T> BuildSurrogate<T>(List<T> collection)
.
So, a quick copy to LinqPad, and a GetRuntimeMethods
run later, it seemed to have all the methods as expected. Naturally, I thought perhaps, something wasn't right with the behavior of GetRuntimeMethod, so, I whipped up a quick extension, GetRuntimeMethodEx
that iterates through, and to my surprise, it failed. What? How could that fail. GetRuntimeMethods has the exact the methodInfo I need.
So, I ended up breaking up the extension into parts to understand what exactly is going on, as in the code below. And it turns out (cType != cGivenType)
always ended up true.
But a quick inspection, shows they were the same 'apparent' type - List<T>
. Now being utterly confused, a diff on the dump of the two typeof(List<T>)
, and it turns out they were not the same!
The MetadataToken
of the two were exactly the same. However the RuntimeTypeHandle
were different. The given type had the correct AssemblyQualifiedName
, and FullName
, belonging to System.Collections.Generic.List``1, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
. Great. But oddly enough, the type on the generic method, had both of them as "null
"! So, basically, the List<T>
is a magical type with no corresponding assembly(!?). It exists, but doesn't
. How fascinating!
Here's a quick dump of the diff between the two, that's relevant.
Note the GenericTypeParameters
, and IsGenericTypeDefinition
- They are the ones which seem to make perfect sense. The oddities aside, now how could one create a Type that matches this type on the MethodInfo? Potentially, the compiler expects a generic type of List<>
with the generic parameter T
- The only problem is, you can't literally make a generic type with T
. The T
has to be a type of something, which now invalidates the equality.
private void Main()
{
var type = typeof (List<>);
var m = typeof (Builders).GetRuntimeMethods();
var surrogateBuilder = typeof (Builders)
.GetRuntimeMethodEx("BuildSurrogate", new[] {type});
}
static class Builders
{
public static Surrogate<T> BuildSurrogate<T>(List<T> collection)
{
return new Surrogate<T>
{
Items = collection.ToArray(),
};
}
public class Surrogate<T>
{
public IEnumerable<T> Items;
}
}
public static class ReflectionExtensions
{
public static MethodInfo GetRuntimeMethodEx(
this Type type, string name, params Type[] types)
{
var m = type.GetRuntimeMethods();
var res = (m.Where(t =>
{
var n = name;
return t.Name.Equals(n);
}).FirstOrDefault(t =>
{
var px = t.GetParameters().ToArray();
var currentTypes = px.Select(p => p.ParameterType).ToArray();
if (currentTypes.Length < 1) return false;
for (var i = 0; i < types.Length; i++)
{
var cGivenType = types[i];
for (var j = 0; j < currentTypes.Length; j++)
{
var cType = currentTypes[j];
if (cType != cGivenType) return false;
}
}
return true;
}));
return res;
}
}
Aucun commentaire:
Enregistrer un commentaire