【问题标题】:Generic delegate instances通用委托实例
【发布时间】:2010-03-12 10:41:41
【问题描述】:

我想知道 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 代表,使其适合“???”部分。

【问题讨论】:

  • C# 也知道 var 这个词,并且会在构建时输入。
  • 你知道你可以 foo.Factory("name 1"),我想。您能否扩展您的问题以解释为什么需要使用 magic 而不是 foo.Factory
  • 你也可以写 'object' :)。对象魔法 = foo.Factory;
  • 我稍微编辑了这个问题,添加了更深层次的原因,并且注意到我确实知道实例上的直接函数调用。
  • @Amby:不,你不能。您无法声明具有未封闭类型的变量。不编译:)

标签: c# generics delegates


【解决方案1】:

这是无法做到的,因为您要声明的是一个未封闭的泛型类型的变量 (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");

【讨论】:

  • 感谢您的回答,我已经怀疑类似的事情了。这就留下了问题的第二部分:“这是 C# 语言中的限制,还是 .NET 框架中的限制”。
【解决方案2】:

一个很好的问题。首先,我们可以观察到 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 中找不到任何明确禁止委托的内容,运行时也会拒绝它。问题是Invoke 上的runtime 属性指定此方法的实现由运行时提供,但当方法是泛型时不支持。虽然ldftn 可以被修改以允许泛型方法和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 的东西,您很可能必须使用System.Reflection.Emit 才能使其具有一定的性能。

【讨论】:

    【解决方案3】:

    就像什么?:

    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");
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多