这个模式有名字吗? (C#编译时类型安全与“params”不同类型的args)

这个模式有名字吗?

假设您要创建一个方法,该方法接受可变数量的参数,每个参数必须是一组固定类型(以任何顺序或组合),以及您无法控制的某些类型。 一种常见的方法是让您的方法接受Object类型的参数,并在运行时validation类型:

void MyMethod (params object[] args) { foreach (object arg in args) { if (arg is SomeType) DoSomethingWith((SomeType) arg); else if (arg is SomeOtherType) DoSomethingElseWith((SomeOtherType) arg); // ... etc. else throw new Exception("bogus arg"); } } 

但是,让我们说,像我一样,你痴迷于编译时类型的安全性,并希望能够在编译时validation你的方法的参数类型。 这是我想出的一种方法:

 void MyMethod (params MyArg[] args) { // ... etc. } struct MyArg { public readonly object TheRealArg; private MyArg (object obj) { this.TheRealArg = obj; } // For each type (represented below by "GoodType") that you want your // method to accept, define an implicit cast operator as follows: static public implicit operator MyArg (GoodType x) { return new MyArg(x); } } 

隐式转换允许您将有效类型的参数直接传递给例程,而无需显式转换或包装它们。 如果尝试传递不可接受类型的值,则会在编译时捕获该错误。

我确定其他人已经使用过这种方法,所以我想知道这种模式是否有名称。

在Interwebs上似乎没有一个命名模式,但根据Ryan对你的问题的评论,我投票的模式名称应该是Variadic Typesafety

一般来说,我会非常谨慎地使用它,但我并不认为这种模式是好还是坏。 许多评论者都提出了优点和优点,我们看到其他模式,如工厂服务定位器dependency injectionMVVM等。这都是关于上下文的。 所以这是一个刺…

上下文

必须处理一组可变的不同对象。

使用时

  1. 您的方法可以接受不同类型的可变数量的参数,这些参数没有公共基类型。
  2. 您的方法被广泛使用(即在代码和/或框架的大量用户的许多地方使用。关键是类型安全提供足够的好处以保证其使用。
  3. 参数可以按任何顺序传递,但不同类型的集合是有限的,并且该方法可接受唯一的集合。
  4. 表现力是您的设计目标,您不希望将责任放在用户身上以创建包装器或适配器(请参阅替代方案 )。

履行

你已经提供了。

例子

  • LINQ to XML(例如new XElement(...)
  • 其他构建器,例如构建SQL参数的构建器。
  • 处理器外观(例如可以接受来自不同框架的不同类型的委托或命令对象的那些)来执行命令而无需创建显式命令适配器。

备择方案

  • 适配器 。 接受某个适配器类型的可变数量的参数(例如, Adapter或非genericsAdapter子类),该方法可以使用该参数来生成所需的结果。 这扩展了您的方法可以使用的集合(类型不再是有限的),但如果适配器做正确的事情以使处理仍然有效,则不会丢失任何内容。 缺点是用户具有指定现有和/或创建新适配器的额外负担,这可能会减损意图(即添加“仪式”,并削弱“本质”)。
  • 删除类型安全 。 这需要接受一个非常基类型(例如Object )并放置运行时检查。 关于要传递的知识的负担传递给用户,但代码仍然具有表现力。 错误直到运行时才会显露出来。
  • 复合 。 传递一个由其他对象组成的对象。 这需要一个预方法调用构建,但是回到使用上述模式之一来复合集合中的项目。
  • 流畅的API 。 用一系列特定的调用替换单个调用,每个类型的可接受参数一个。 一个典型的例子是StringBuilder

它被称为反模式,通常被称为恶作剧者 。

更新:

如果args的类型总是不变并且顺序无关紧要,那么创建重载,每个重载都采用集合(IEnumerable )在每个方法中为您需要操作的类型更改T. 这将通过以下方式降低代码复杂性:

  1. 删除MyArg类
  2. 无需在MyMethod中进行类型转换
  3. 将添加额外的编译时类型安全性,如果您尝试使用无法处理的args列表调用该方法,您将获得编译器exception。

这看起来像复合模式的一个子集 。 引自维基百科:

复合模式描述了一组对象的处理方式与对象的单个实例相同。