在循环中调用一个泛型方法,不同类型在编译时已知。

5

让我们考虑一个接口中的方法:

public Interface IA {
    T[] Get<T>() where T : IB;
}

在另一个地方,我希望能够为n种已知的编译时类型调用此方法n次。以下代码说明了我的意图。

foreach(Type t in new Type[] { typeof(X), typeof(Y), typeof(Z) }) 
{
    InterfaceXYZImplement[] arr = c.Resolve<IA>().Get<t>();
    //...more here
}

现在,显然使用foreach循环会使类型变成运行时值,因此我必须使用MakeGenericMethod
有没有一种方法可以编写代码,以便我可以针对XYZ执行代码,但只调用一次编写的方法?
将代码包装在方法中只会将问题向上移动(这是部分解决方案,但不是最终解决方案)。

2
为什么不一个一个地调用它们?编译时类型是否经常变化? - leppie
我并不真正清楚“仅调用一次编写的方法”是什么意思,以及@mrtofigh的解决方案有什么问题 - 你能详细说明一下吗?使用MakeGenericMethod有什么问题吗? - AakashM
@leppie 因为这个列表可能会在其他地方使用,比如我有一个文件中存储着一系列类型,系统的不同部分会根据这些类型执行不同的操作。当我添加一个类型时,我需要找到所有调用它们的地方并进行修改。 - tymtam
  1. 我的意思是只调用该方法一次,而不是三次。
  2. MakeGenericMethod需要一个字符串,但字符串是邪恶的。
- tymtam
如果存在 n 种类型,则必须发生 某些事情 n 次,我不确定为什么您不希望该某些事情是一个方法调用。并且,如果您不想在传递给 MakeGenericMethod 的方法名称上进行硬编码,可以尝试使用 delegateExpression 进行操作。 - AakashM
2个回答

3
你可以使用一些辅助方法来实现这一点,它们做出了许多假设,并使用 .NET4 中的新 dynamic 关键字。
基本上,该解决方案利用 DLR 的特性,当使用类型为 dynamic 的参数时,正确推断要用于泛型方法的类型。 为了使其工作,您需要两个辅助方法:
IB[] Get(IA a, Type t)
{
    dynamic dummy = Activator.CreateInstance(t);
    return Get(a, dummy);
}

T[] Get<T>(IA a, T dummy) where T : IB
{
    return a.Get<T>();
}

你的代码将调用这些辅助方法中的第一个:

foreach(var t in new Type[] { typeof(X), typeof(Y), typeof(Z) })
{
    IB[] arr = Get(c.Resolve<IA>(), t);
    // do more stuff here
}

正如您所看到的,这种方法有几个缺点:

  1. 在循环中为每种类型创建一个新实例,使用 Activator.CreateInstance(t)。此行假设存在一个可用的公共无参数构造函数,并且该构造函数不执行任何重要操作。
  2. 如果循环中的任何类型未实现 IB,则此代码将在运行时抛出异常。没有编译错误。
  3. 结果始终是 IB 实例的数组,而不是特定类型的数组。

3

定义扩展方法:

public static class Extensions
{
    public static void Get<T>(this IA a, Action<T[]> action, params Type[] types) where T : IB
    {
        foreach (var type in types)
        {
            var method = a.GetType().GetMethod("Get").MakeGenericMethod(type);
            var ts = (T[])method.Invoke(a, null);
            action(ts);
        }
    }
}

使用:

a.Get(arr => { 
   // work on result
}, typeof(X), typeof(Y), typeof(Z));

魔法字符串!啊啊啊!转换!啊啊! - tymtam

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