C# 这个图案有名字吗?(C“编译时类型安全性,带有不同类型的“params”参数)

C# 这个图案有名字吗?(C“编译时类型安全性,带有不同类型的“params”参数),c#,params,type-safety,C#,Params,Type Safety,这个图案有名字吗 假设您希望创建一个方法,该方法接受数量可变的参数,每个参数必须是固定类型集(以任何顺序或组合)中的一个,并且某些类型您无法控制。一种常见的方法是让您的方法获取Object类型的参数,并在运行时验证这些类型: void MyMethod (params object[] args) { foreach (object arg in args) { if (arg is SomeType) DoSomethingWith((S

这个图案有名字吗

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

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");
    }
}
但是,假设您和我一样,痴迷于编译时类型安全,希望能够在编译时验证方法的参数类型。我想出了一个办法:

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

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


我相信其他人也使用过这种方法,所以我想知道这种模式是否有一个名称。

这看起来像是。引用维基百科:


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

互联网上似乎没有命名模式,但根据对您的问题的评论,我认为模式的名称应为类型安全性

一般来说,我会非常谨慎地使用它,但我不会判断模式是好是坏。许多评论者在正反两方面都提出了很好的观点,我们看到了其他模式,如工厂、服务定位器、依赖项注入、MVVM等。这些都与上下文有关。所以这里有一个尝试

上下文

必须处理不同对象的变量集

  • 您的方法可以接受不同类型的变量数量,这些参数没有公共的基类型
  • 您的方法被广泛使用(例如,在代码中的许多地方和/或框架的大量用户)。重点是类型安全性提供了足够的好处来保证它的使用
  • 参数可以按任何顺序传递,但不同类型的集合是有限的,并且是该方法唯一可以接受的集合
  • 表现力是您的设计目标,您不想让用户承担创建包装器或适配器的责任(请参见备选方案)
  • 实施

    你已经提供了

    示例

    • LINQ到XML(例如,
      新的XElement(…)
    • 其他构建器,如构建SQL参数的构建器
    • 处理器外观(例如,可以接受来自不同框架的不同类型的委托或命令对象的外观)以执行命令,而无需创建显式命令适配器
    备选方案

    • 适配器。接受方法可用于产生所需结果的某些适配器类型(例如,
      Adapter
      或非泛型
      适配器的子类)的可变数量的参数。这拓宽了方法可使用的集合(类型不再是有限的),但如果适配器做了正确的事情以使处理仍能工作,则不会损失任何东西。缺点是用户在指定现有适配器和/或创建新适配器方面有额外的负担,这可能会降低意图(即添加“仪式”,并削弱“本质”)
    • 删除类型安全性。这需要接受一个非常基本的类型(例如,
      对象
      )并进行运行时检查。要传递的内容的负担会传递给用户,但代码仍然是表达性的。直到运行时,错误才会显露出来
    • 组合。传递一个由其他对象组合而成的单个对象。这需要预先建立方法调用,但会返回到对组合集合中的项使用上述模式之一
    • Fluent API。将单个调用替换为一系列特定调用,每种类型的可接受参数对应一个调用。一个典型示例是
      StringBuilder

    它被称为反模式,通常称为

    更新:

    如果参数的类型始终为常量,且顺序无关紧要,则创建重载,每个重载都采用集合(IEnumerable),并在每个方法中为需要操作的类型更改T。这将通过以下方式降低代码复杂性:

  • 删除MyArg类
  • 消除MyMethod中类型转换的需要
  • 将添加一个额外的编译时类型安全性,因为如果您尝试使用无法处理的参数列表调用该方法,则会出现编译器异常

  • 我可能遗漏了什么,但我没有看到模式。你知道,你可以让“必需类型”实现一个公共接口,然后使用
    params-IWhatever[]args
    @Etienne您不能将接口改装到
    字符串等东西上。Ed如果您必须想出一个不同寻常的解决方案,我认为这是过于工程化了,因为这通常意味着您最初处理问题的方式是错误的。更简单的解决方案是不需要使用类型切换板。你知道这让我想起了什么吗?PHP。尽管PHP是动态类型,而C#是静态类型。可能,这是我所知道的最适合这种情况的GoF模式。我不同意,这可能在库中有用。类型安全ftw。此构造函数“private MyArg(object obj){this.TheRealArg=obj;}”消除编译时类型安全的任何可能性。此外,MyMethod中的强制转换消除了DoSomethingWith和DoSomethingElseWith方法免费获得的编译时类型安全。呃,如果它们不是常量呢?我想这样调用我的函数:
    f(10,“hello”,新异常(“bugger it”))
    ,我只希望它接受这些类型。只有有效的参数