通用委托实例

时间:2010-03-12 10:41:42

标签: c# generics delegates

我想知道C#(或底层的.NET框架)是否支持某种“泛型委托实例”:这是一个委托实例仍然有一个未解析的类型参数,要在调用委托时解析(不是在创建代表时)。我怀疑这是不可能的,但无论如何我都在问它......

这是我想要做的一个例子,有些“???”插入C#语法似乎无法满足我想要的地方。 (显然这段代码不能编译)

class Foo {
  public T Factory<T>(string name) {
    // implementation omitted
  }
}

class Test {
  public void TestMethod()
  {
    Foo foo = new Foo();
    ??? magic = foo.Factory; // No type argument given here yet to Factory!
                             // What would the '???' be here (other than 'var' :) )?
    string aString = magic<string>("name 1"); // type provided on call
    int anInt = magic<int>("name 2"); // another type provided on another call

    // Note the underlying calls work perfectly fine, these work, but i'd like to expose
    // the generic method as a delegate.
    string aString2 = foo.Factory<string>("name 1");
    int anInt2 = foo.Factory<int>("name 2");
  }
}

有没有办法在C#中实际做这样的事情?如果没有,那是语言的限制,还是在.NET框架中?

编辑: 我问的原因是因为我想将委托传递给另一个程序集中的函数,并且不希望要求其他程序集必须引用任何特定类型(在我的示例中为“Foo”类)。我希望弯曲标准的Func&lt;&gt;以某种方式委托,以适应“???”一部分。

3 个答案:

答案 0 :(得分:7)

这是不可能的,因为你要求的是声明一个未闭合的泛型类型的变量(magic)。

可以使用未闭合的泛型,但仅限于类型级别,例如:

delegate T FactoryDelegate<T>(string name);

var magicType = typeof (FactoryDelegate<>);

然后在稍后“关闭”该类型:

var stringMagic = magicType.MakeGenericType(typeof(string));

更新:说,这里有一个示例,说明如何使用上述技术来处理未闭合的方法“类型”。如果我们可以分配未闭合的类型,那么仍然没有那么优雅..:

    public class UnclosedMethod
    {
        private readonly MethodInfo _method;

        public UnclosedMethod(Type type, string method)
        {
            _method = type.GetMethod(method);
        }

        public T Invoke<T>(string name)
        {
            var fact = _method.MakeGenericMethod(typeof(T));
            return (T)fact.Invoke(this, new object[] { name });
        }
    }

然后在代码中执行此操作:

var magic = new UnclosedMethod(typeof(Foo), "Factory");
var x = magic.Invoke<string>("bar");

答案 1 :(得分:0)

就像?:

Foo foo = new Foo();
string aString = 
   foo.GetType().GetMethod("Factory").MakeGenericMethod(string)
       .Invoke(foo, new object[] { "name 1" });
int anInt = 
   foo.GetType().GetMethod("Factory").MakeGenericMethod(int)
       .Invoke(foo, new object[] { "name 2" });

现在,如果你想使用一个委托,你可能会得到类似的东西:

public delegate T FactoryDelegate<T>(string name);

然后,您可以拨打电话:

public TestMethod1(FactoryDelegate<dynamic> factory)
{
    object o = factory("name 3");
}

答案 2 :(得分:0)

一个好问题。首先,我们可以看到C#不允许您使用通用的Invoke方法定义任何委托类型。类型参数根本没有空间。代表名称后面的列表用于代表类型本身的参数。

因此,我参加了CIL并生成了看起来像是带有通用Invoke的委托:

.class public auto ansi sealed GenericDelegate extends [mscorlib]System.MulticastDelegate
{

.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed
{
}

.method public hidebysig newslot virtual instance !!T Invoke<T>(!!T arg) runtime managed
{
}

}

令我惊讶的是,C#实际上可以使用这种类型而不会出现问题–您可以从通用方法(具有匹配约束)创建这种类型的实例,然后程序可以编译。但是,结果是无效的CIL,因为构造委托使用ldftn指令,但是泛型方法没有与之关联的可执行代码,因为它是泛型的。

即使我在ECMA-335中找不到任何明显禁止委托的内容,运行时也会拒绝它。问题是runtime的{​​{1}}属性指定运行时提供此方法的实现,但是当该方法是通用方法时不支持此方法。尽管可以修改Invoke 以允许通用方法,并且在这种情况下可以提供ldftn 的实现,但事实并非如此。< / p>


但是,我同意有时这个概念很有用。尽管运行时无法为您提供帮助,但最简单的方法可能只是使用接口:

Invoke

为所需的每个参数变体创建一个接口,并为每个需要调用的方法创建一个实现。如果您还想拥有类似于class Foo { public T Factory<T>(string name) { } } class FooFactory : IGenericFunc<string> { readonly Foo target; public FooFactory(Foo target) { this.target = target; } public T Invoke<T>(string name) { return target.Factory<T>(name); } } interface IGenericFunc<TArg> { T Invoke<T>(TArg arg); } 的功能,则很有可能必须使用Delegate.CreateDelegate使其具有一定的性能。