【问题标题】:a constructor as a delegate - is it possible in C#?构造函数作为委托 - 在 C# 中是否可能?
【发布时间】:2009-10-21 13:04:51
【问题描述】:

我有一个像下面这样的课程:

class Foo
{
  public Foo(int x) { ... }
}

我需要将这样的委托传递给某个方法:

delegate Foo FooGenerator(int x);

是否可以直接将构造函数作为FooGenerator 值传递,而不必键入:

delegate(int x) { return new Foo(x); }

?

编辑:供我个人使用,该问题涉及 .NET 2.0,但也欢迎 3.0+ 的提示/响应。

【问题讨论】:

  • 有趣的问题。我相信就 CLR 而言,构造函数是有效的方法,但我不知道语法。
  • 我很感兴趣:你为什么要这么做?
  • 我怀疑答案是否定的。
  • @Mitch Wheat:详细信息:1)我有类 'Deriv1' 和 'Deriv2' 扩展了 'Foo',2)我有一个方法 'SomeLogic()' 有时需要创建一个基于 'x' 的 'Foo' 派生对象,3) 我想在调用该方法时决定它应该是 'Deriv1' 还是 'Deriv2'。既然我想到了这一点,我可能想考虑使用模板方法......但要作为一个答案,我宁愿打开一个单独的问题:) +1 & 向你致敬:)

标签: c# delegates constructor


【解决方案1】:

我假设您通常会在工厂实现中执行类似的操作,其中实际类型在编译时是未知的...

首先,请注意,更简单的方法可能是创建后初始化步骤,然后您可以使用泛型:

static T Create<T>({args}) where T : class, ISomeInitInterface, new() {
    T t = new T();
    t.Init(args);
    return t;
}

然后您可以使用MakeGenericMethod 和/或CreateDelegate


否则;您可以使用 Expression (3.5) 或 DynamicMethod (2.0) 即时执行此操作。

Expression 方法更容易编码:

    var param = Expression.Parameter(typeof(int), "val");
    var ctor = typeof(Foo).GetConstructor(new[] { typeof(int) });
    var lambda = Expression.Lambda<Func<int, Foo>>(
        Expression.New(ctor, param), param);
    var func = lambda.Compile();
    Foo foo = func(123);
    string s = foo.ToString(); // proof

或(使用DynamicMethod):

    ConstructorInfo ctor = typeof(Foo).GetConstructor(new[] { typeof(int) });
    DynamicMethod dm = new DynamicMethod("Create", typeof(Foo),
            new Type[] { typeof(int) }, typeof(Foo), true);
    ILGenerator il = dm.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Newobj, ctor);
    il.Emit(OpCodes.Ret);
    Converter<int, Foo> func = (Converter<int, Foo>)
        dm.CreateDelegate(typeof(Converter<int, Foo>));        
    Foo foo = func(123);
    string s = foo.ToString(); // proof

【讨论】:

  • 哦哦;从技术上讲,使用反射 &c。是正确的(我也确实考虑了一会儿),但它有严重的缺陷:1)如您的评论中所见,它严重损害了可读性(并使代码不那么简洁而不是更简洁); 2) AFAIK,反射比语言支持的结构慢,因为它堆叠了一层抽象。
  • 一旦编译为委托,基于反射的方法并不慢,并且(有时)可以比常规编译的代码更快。显然你只编译一次并缓存委托。
  • +1。这(表达式实现)对我来说比接受的答案更有帮助,因为我需要 cctor,而不是 ctor。
  • +1 这显然比公认的答案好!谢谢!
  • 很好的答案,但我不禁说“哎哟”在 Java 中你可以做 Function&lt;Integer, Foo&gt; ctor = Foo::new。请不要误会我的意思,我讨厌 Java。
【解决方案2】:

不,CLR 不允许将委托绑定到 ConstructorInfo

但是,您可以创建自己的:

static T Make<T>(Action<T> init) where T : new()
{
  var t = new T();
  init(t);
  return t;
}

用法

var t = Make<Foo>( x => { x.Bar = "bar"; x.Baz = 1; });

【讨论】:

  • 您能否为您关于绑定到 ConstructorInfo 的声明添加一些参考资料(MSDN 链接?)?
  • 构造函数不会产生新对象。构造函数与分配例程一起工作。
  • @sixlettervariables: +1 - 这看起来是一个合理的解释。也就是说,我仍然希望看到一些 MSDN/C#-specification/... 来自某人的参考。
  • @akavel:我需要吗? msdn.microsoft.com/en-us/library/…,深入研究 Reflector,看看它需要一个 RuntimeMethodHandle。
  • 另一种可能很狡猾的解决方案是通过一组public static &lt;SELF&gt; Create&lt;SELF&gt;(/* args */) 方法镜像所有的ctors。因此你可以说Func&lt;string, string, Foo&gt; = Foo.CreateFoo
【解决方案3】:

我认为尽可能简洁(无需迁移到工厂模式)将是匿名方法,如下所示:

delegate Foo FooGenerator(int x);

...    

void DoStuff()
{
    YourDelegateConsumer(x => new Foo(x));
}

这并没有严格按照您的要求进行(因为您将委托传递给返回新实例的匿名方法,而不是直接委托给构造函数),但我不认为您是什么重新要求是完全可能的。

当然,这是假设您使用的是 3.5+

【讨论】:

  • +1;我实际上是在为 2.0 编译,这就是为什么我必须使用“委托”,但由于核心问题是关于其他的,所以肯定应该记住 lambda 构造。
  • 是的,我认为这是在“现代”C# 中最优雅、最简洁的方式。测试了一下,完美运行! +1
【解决方案4】:

听起来您可能想使用类工厂模式。

Factory Method Pattern

【讨论】:

  • 这实际上是我用作解决方法的方法,但在编写问题时,我认为“代表”结构更容易理解。
  • 工厂方法模式很好,但前提是您在编译时知道所有可能的类型。
【解决方案5】:

不幸的是,构造函数与方法并不完全相同,因此您无法创建指向它们的委托。不过,这是一个有趣的想法,也许有了更多信息,我们可以设计出某种在语法上相似的解决方法。

【讨论】:

  • 您能否详细说明在委托的上下文中“构造函数不完全(...)作为方法”的陈述?我特别喜欢对 MSDN/C# 参考/其他文档的一些参考。
  • 无响应的答案,提供的唯一信息是“否”和“构造函数与方法不完全相同”(当然不是)。是的,另一个答案假装通过说“它不是”来解释为什么某事是不可能的。
【解决方案6】:

Marc Gravell 的回答启发了我以下非常简单的解决方案:

static void Main()
{
    Pet a = _MakeObject(typeof(Dog));
    Pet b = _MakeObject(typeof(Cat));
}

private static Pet _MakeObject(Type type)
{
    ConstructorInfo info = type.GetConstructor(new Type[0]);
    return (Pet)info?.Invoke(null);
}

如果您的构造函数有参数(在此示例中:1 个 int 类型的参数),则几乎相同:

static void Main()
{
    Pet a = _MakeObject(typeof(Dog), 5);
    Pet b = _MakeObject(typeof(Cat), 7);
}

private static Pet _MakeObject(Type type, int age)
{
    ConstructorInfo info = type.GetConstructor(new [] { typeof(int) });
    return (Pet)info?.Invoke(new object[] { age });
}

【讨论】:

    【解决方案7】:

    另一种选择是使用Activator 类,如下所示:

    使用泛型类型

    public delegate Foo FooGeneratorDelegate<T>(int x);
    
    public T FooGeneratorFunction<T>(int x)
    {
        return (T)Activator.CreateInstance(typeof(T), x);
    }
    
    // implementation example
    FooGeneratorDelegate<Foo> del = FooGeneratorFunction<Foo>;
    Foo foo = del(someIntValue);
    

    将您的类型 Foo 作为参数传递

    public delegate object FooGeneratorDelegate(Type t, int x);
    
    public object FooGeneratorFunction(Type t, int x)
    {
        return Activator.CreateInstance(t, x);
    }
    
    // implementation example
    FooGeneratorDelegate del = FooGeneratorFunction;
    Foo foo = (Foo)del(typeof(Foo), someIntValue);
    

    如果类型将始终为 Foo 类型

    public delegate Foo FooGeneratorDelegate(int x);
    
    public Foo FooGeneratorFunction(int x)
    {
        return (Foo)Activator.CreateInstance(typeof(Foo), x);
    }
    
    // implementation example
    FooGeneratorDelegate del = FooGeneratorFunction;
    Foo foo = del(someIntValue);
    

    【讨论】:

      【解决方案8】:

      而其他答案仅支持无参数构造函数。这是版本支持参数。希望它可以帮助想要将参数 ConsturctorInfo 转换为 Delegate 的人。

      public Delegate CreateDelegate(ConstructorInfo constructorInfo)
      {
          var ctorParameters = constructorInfo.GetParameters();
          var lambdaParameters =
              ctorParameters.Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToArray();
          var argsExp =
              ctorParameters.Select((p, i) => Expression.Convert(lambdaParameters[i], p.ParameterType));
          var newExp = Expression.New(constructorInfo, argsExp);
          var lambda = Expression.Lambda(newExp, lambdaParameters);
          return lambda.Compile();
      }
      

      用法:

      CreateDelegate(/* ConstructorInfo */).DynamicInvoke(args);
      

      【讨论】:

        【解决方案9】:

        我的猜测是这是不可能的,因为你会传递一个尚未创建的对象的方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-01-16
          • 1970-01-01
          • 2020-09-02
          • 1970-01-01
          • 2010-12-29
          相关资源
          最近更新 更多