获取泛型对象参数的实际类型

10
毫无疑问,这个问题的某些方面以前已经被问过了,但是我很难找到答案。(免责声明:这与我最近提出的一个问题有关,但是是分开的)。
我有一个像这样的方法:
public static void Method<T>(MethodInfo m, T value)
{
  Type memberType = m.GetValueType();

  if (memberType.IsAssignableFrom(typeof(List<T>))
  {
    object memberValue = Activator.CreateInstance(memberType);
    ((List<T>)memberValue).Add(value);
  }
}

当我像这样调用它时,它可以正常工作:

string s = "blah";
Method(memberInfo, s);

然而,我需要使用通用类型调用此方法,因此我会像这样调用它:
Type valueType = someType;
object passValue = someMethod.MakeGenericMethod(new Type[] { valueType }).Invoke(this, new object[] { });
/* Call my original method */
Method(memberInfo, passValue );

现在,Intellisense知道Method<T>中的'value'是什么类型(比如 'FooObject')。但是'T'是object类型,这意味着List<FooObject>不能赋值给List<T>(即List<object>)。
我之前尝试使用Convert.ChangeType对变量('passValue')进行转换,但没有更多用处。
由于无法将变量强制转换为类型变量的类型,我该怎么解决这个问题?
最好的解决方案是不要依赖IsAssignableFrom,并进行松散的类型检查以确定是否可以工作。问题在于,除非'T'真正是memberValue的元素类型,否则我不确定能否正确地将memberValue进行转换。

GetValueType()是你代码中的扩展方法。但即使没有看到它的代码,似乎Method<T>方法并没有做任何事情?我想给你一个改进的解决方案,但我真的不知道你在这里想做什么。 - Sam Harwell
是的,抱歉getvaluetype只是一种方法,用于根据特定的MemberType调用FieldType/PropertyType给定的MemberInfo。Method<T>将一个对象附加到表示列表的MemberInfo上(即表示List的Field或Property)。 - Matt Mitchell
2个回答

6

你很幸运。几周前,我实际上不得不做类似的事情

有关详细说明,请参见上面的博客文章,但基本思路是反射类型并手动使用明确的参数集调用方法。

typeof(MyClass).GetMethod("Foo").MakeGenericMethod(new[] { param.GetType() }).Invoke(null, new[] { param });

虽然它不是非常类型安全,但它确实可以完全满足你的需求。

class Program
{

    static void Main(string[] args)
    {
        object str = "Hello World";
        object num = 5;
        object obj = new object();

        Console.WriteLine("var\tvalue\t\tFoo() Type\tCallFoo() Type");
        Console.WriteLine("-------------------------------------------------------");
        Console.WriteLine("{0}\t{1}\t{2}\t{3}", "str", str, MyClass.Foo(str), MyClass.CallFoo(str));
        Console.WriteLine("{0}\t{1}\t\t{2}\t{3}", "num", num, MyClass.Foo(num), MyClass.CallFoo(num));
        Console.WriteLine("{0}\t{1}\t{2}\t{3}", "obj", obj, MyClass.Foo(obj), MyClass.CallFoo(obj));
    }

}

class MyClass
{
    public static Type Foo<T>(T param)
    {
        return typeof(T);
    }

    public static Type CallFoo(object param)
    {
        return (Type)typeof(MyClass).GetMethod("Foo").MakeGenericMethod(new[] { param.GetType() }).Invoke(null, new[] { param });
    }

}

输出

   var     value           Foo() Type      CallFoo() Type
   -------------------------------------------------------
   str     Hello World     System.Object   System.String
   num     5               System.Object   System.Int32
   obj     System.Object   System.Object   System.Object

那花费我的时间比我愿意承认的要长,我很高兴我能为你节省时间!编程愉快 :) - Nathan Taylor

6
这应该会给你一个可调用的方法(我稍后会测试它)。它所涉及的装箱/拆箱比反射API调用所需的安全检查要快得多(后者也需要装箱)。
private static Action<MethodInfo, object> BuildAccessor(Type valueType)
{
    MethodInfo genericMethod = null; // <-- fill this in
    MethodInfo method = genericMethod.MakeGenericMethod(new Type[] { valueType });
    ParameterExpression methodInfo = Expression.Parameter(typeof(MethodInfo), "methodInfo");
    ParameterExpression obj = Expression.Parameter(typeof(object), "obj");

    Expression<Action<MethodInfo, object>> expr =
        Expression.Lambda<Action<MethodInfo, object>>(
            Expression.Call(method, methodInfo, Expression.Convert(obj, valueType)),
            methodInfo,
            obj);

    return expr.Compile();
}

看起来这也应该可以工作 :-) 谢谢! 似乎关键在于动态调用通用方法以正确转换类型。 - Matt Mitchell
你能演示一下吗?我有点不确定它需要如何调用。 - Nathan Taylor
你是在动态调用方法而不是直接调用,以便类型能够自行解析。我只是想象这个做法是在做类似的事情。 - Matt Mitchell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接