【问题标题】:C# pass a delegate an argument list for a class constructor in a fairly concise wayC# 以相当简洁的方式向委托传递类构造函数的参数列表
【发布时间】:2012-03-11 03:05:08
【问题描述】:

在 C# 中,我能否以相当简洁的方式向委托传递类构造函数的参数列表?

我知道这是非常特殊的情况,可能看起来有点奇怪,也不是工厂模式。如果它有助于将其视为挑战。这个问题的正确答案可能是不可能。

Func<MyClassConstructionArgsType, MyClass> make_MyClass =
    args => {return new MyClass(args);};

var n = make_MyClass(comma separated arguments);

我还需要不要有参数描述的副本,例如以下不是解决方案:

Func<int, string, MyClass> make_MyClass =
    (a, b) => {return new MyClass(a, b);};

或者,出于同样的原因:

Class Args
{
    ...
}

Func<Args, MyClass> make_MyClass =
    a => {return new MyClass(a);};

var n = make_MyClass(Args(args));

Dito 是这种情况:

var n = make_MyClass<MyClass>(comma separated arguments);

Object[]{逗号分隔参数}方法很好,除了还需要支持可选参数。

这个问题是根据 Anastasiosyal 对以下问题的回答创建的:c# class reference as opposed to instance reference

Josh 第一个答案似乎在 C# 中尽可能接近。

【问题讨论】:

  • 匿名投票者请使用 cmets,先回答或编辑。
  • 我没有投反对票,但请参阅this post for more info
  • 您要求的不是特别清楚。您能否举例说明您喜欢它如何工作,也许我们可以从那里开始?另外,您要解决的根本问题是什么?我认为这与您最后一个问题中令人讨厌的类嵌套有关。我当然会用你给出的后两个例子之一来回答。为什么它们不合适?
  • 第一个示例显示了我希望它如何工作,在定义“make_MyClass”之后,然后“var n = make_MyClass(逗号分隔的参数);”可以使用,例如它可以是“var n = make_MyClass(4, "abc");”,但在帖子中列出的附加要求是不要重复有关委托声明中的参数的信息。
  • 听起来你太努力避免使用 ConstructorInfo.Invoke()。这需要一个对象[]。

标签: c# class delegates constructor arguments


【解决方案1】:

我仍然不是 100% 清楚您在这里寻找什么,但我会尝试一下。

我假设您要创建一个调用类的构造函数的委托,并返回该类的新实例。这样做的简单方法是:

var makeX = new Func<String, Integer, X>((s,i)=>new X(s,i));

// To use it
var myX = makeX("Foo", 5);

我不确定为什么在您的用例中这不是“可接受的”。如果您能指出这种方法的具体技术或语法问题,我会尽力解决。

编辑:

假设可以接受预定义自己的委托:

delegate X makeX(String s, Integer i);

那么你可以这样做:

var myMakeX = new makeX((s,i)=>new X(s,i));

// To use it
var myX = myMakeX("Foo", 5);

但我怀疑这是你能得到的最短的时间。现在,如果您提前知道,例如,所有时间都想将“Foo”指定为 s 的值,那么您可以创建另一个短路委托(使用称为 @987654321 的技术) @):

var myCurryMaker = new Func<String, Func<Integer, X>>(s => return new Func<Integer, X>(i => new X(s, i)));
var myCurriedX = myCurryMaker("Foo");

// To use it
var myX = myCurriedX(5);

【讨论】:

  • “字符串,整数”部分并​​不理想,如果它可能是 typeof(X).ArgTypes 之类的东西。
  • 如果没有 String, Integer 部分,您将失去类型安全性。我会再添加一个可能的替代方案。
  • 所以你想要失去类型安全?
  • 静态、动态、强类型和弱类型是不同的。我可以看到您对哪些组合安全与否有看法。我不想在这里讨论类型讨论的方法。看看 Alan Kay 对获得诺贝尔计算奖的问题的看法,以及可能在较小程度上 Eric Lippert 的看法,尽管我根本不想错误引用 Eric,所以我不会给出例子。总的来说,我是安全的拥护者,即使它会导致提高可证明性不切实际,或者我们必须重新考虑一些事情。但这并不意味着静态类型的事情会更安全。
【解决方案2】:

并非没有某种形式的反思。

您可以创建一个使用 Activator 的委托:

Func<Object[], MyClass> makeClass =
   args => (MyClass) Activator.CreateInstance(typeof (MyClass), args);

makeClass(new object[] {"String", 32});

但这是尽可能接近使用委托的方式。但是,如果你创建一个静态帮助器,你可以让它看起来更漂亮:

public static T BuildType<T>(params Object[] args) {
   return (T) Activator.CreateInstance(typeof (T), args);
}

BuildType<MyClass>("String", 32);

再一次,如果没有反思,我不确定你是否能真正实现你想要的。

【讨论】:

  • 我只是添加一个评论,我不确定您从这段代码中获得了什么。显然,您在编译时就知道您想要什么类型,因此无需委托该类型的构造。此外,即使这是在运行时完成的事情,它也代表了一个根本不需要的间接级别。只需调用 Activator.CreateInstance() 即可完成。
  • +1:很好的辅助方法,我认为它与 OP 的想法(或者你可以用 C# 实现的目标)非常接近。
  • 正如@Josh 上面提到的,非常接近Activator.CreateInstance。无论哪种方式,您都会失去类型安全性,这真的很痛苦。我会立即从我的项目中删除该代码。
  • 经过测试,第一个很棒,只是它不支持可选参数。 Object[]{0.2f, abc: 42},由于 BuildType 行 的内容,第二个不太好。我编辑了第二个不需要模板,但仍然有可选参数的东西。
【解决方案3】:

下面的一些事情怎么样:

public delegate object VariableParamFactoryFunc(params object[] constructorParams);

public class Factory
{
    private Dictionary<Type, VariableParamFactoryFunc> _registeredTypes = new Dictionary<Type, VariableParamFactoryFunc>();

    public void RegisterType<T>(VariableParamFactoryFunc factoryFunc)
    {
        _registeredTypes.Add(typeof(T), factoryFunc);
    }

    public T Resolve<T>(params object resolutionParams)
    {
        VariableParamFactoryFunc factoryFunc;
        if (_registeredTypes.TryGetValue(typeof(T), out factoryFunc))
        {
            return (T)factoryFunc();
        }
        else
        {
            return default(T);
        }
    }
}

然后您将通过首先注册工厂函数来使用它:(例如)

var factory = new Factory() // or you can turn it into a singleton, this is just for demonstration
factory.Register<IMyInterface>((a, b, c) => new ConcreteImplementation(a, b, c))
factory.Register<CustomClass>((a, b) => new CustomClass(a, b));

然后通过实际解析和使用这些工厂函数:

IMyInterface myInterface = factory.Resolve<IMyInterface>(1, "string", 2.412);
CustomClass customClass = factory.Resolve<CustomClass>("param1", 1234);

这应该允许您灵活地解析具有可变数量参数的构造函数,而不会支付 Activator.CreateInstance 或 Reflection 的性能损失,但是您会失去类型安全性,因为所有参数都将被强制转换为对象并且您需要将它们强制转换回到工厂函数中的正确类型

祝你好运!

【讨论】:

  • 开始理解和实现很多,在我开始之前,它是否适用于参数列表中由名称引用的可选参数...,名称:值,...?跨度>
  • 另外,CustomClass 不像 MyType 是吗?我根本不需要在使用它的行上引用类型名称。
  • 这不适用于命名的可选参数,它还要求您指定要使用它的类型,所以我猜这不是您真正想要的
  • 无论如何,谢谢,如果可能的话,我会给你一些代表点来解决问题。
猜你喜欢
  • 2016-10-31
  • 1970-01-01
  • 1970-01-01
  • 2011-06-23
  • 2010-12-29
  • 1970-01-01
  • 1970-01-01
  • 2017-07-09
相关资源
最近更新 更多