编译时检查值以实现多个接口

本文关键字:接口 实现 检查 编译 | 更新日期: 2023-09-27 18:32:31

假设我想表达">一个期望实现接口IFoo和IBar的对象的方法"。像这样:

void Method(IFoo+IBar param);

我怎么能用 C# 这么说呢?

(有语法结构吗?或者也许是一个好的成语?


一次尝试

介绍一个接口:

interface IFooBar : IFoo, IBar {}
void Method(IFooBar param);

这很糟糕,我什至后悔想到它:-(乍一看看起来还可以,但可悲的是,它默默地引入了不应该在这里的依赖关系。

IFooBar存在的唯一原因是成为Method界面的一部分。因此,任何用作Method参数的对象类都必须知道此方法(和该接口(的存在 - 否则情况并非如此。所有实现 IFoo 和 IBar 的类都需要修改以实现 IFooBar(可能了解一个新的程序集( - 这是非常不切实际的,如果我们不能修改它们,甚至是不可能的。

不必要的依赖 = 不行。

解决方法

放弃静态类型:

void Method(object param)
{
    if (!param is IFoo)
        throw new ArgumentException("IFoo not supported", "param");
    if (!param is IBar)
        throw new ArgumentException("IBar not supported", "param");
    // ...
}

(或者:在签名中定义一种类型并动态检查其他类型 - 如果一种是最重要的,则更可取,但更令人困惑(

工作正常,但在运行时(无编译时检查(。迫切需要一个文档(每个人都要阅读它(。

也只在功能参数上实用。如果我尝试在字段上使用它,代码会因强制转换而变得非常臃肿。


这种情况的真实案例?例如 IList<Foo> + INotifyCollectionChanged .

编译时检查值以实现多个接口

public void someMethod<T>(T param) where T : IFoo, IBar
{...}

我的一个朋友曾经问过埃里克·利珀特一个非常相似的问题,这是他的回答:

问:是否有原因 C# 不允许通过以下方式声明变量 多个接口?

答:假设我们将其注释为 {IFoo, IBar}。

问题比声明变量更大。本质上你是 说您想将变量的约束从"The 变量必须是给定类型"到"变量必须是全部 几种给定的类型"。但为什么要止步于变量呢?如果是这种类型 变量的约束,那么它应该是一个类型,句点。 你 应该可以说:

List< { IFoo, IBar } > myList; Or
 public static { IFoo, IBar } MyMethod( { IFoo, IBar }[ ] foobars  )
 {  return foobars[0];  }

等等。半途而废没有意义。

这将是类型系统的主要扩展。我们想要 支持每种托管语言,而不仅仅是 C# 和 VB。这是 您希望在版本中进入框架的功能类型 一;做出如此重大的改变是非常昂贵和困难的 当您有四个版本并拥有数十亿条客户线时 必须继续工作的代码。

我自己经常想要这个功能;我同意这是一个很好的 功能,但我认为现在添加它太贵了。 下一个 在设计新类型系统时,请从 开始!

您可以通过定义方法来实现它,如下所示

void Method<T>(T param) 
    where T: IFoo, IBar
{
}

希望这有帮助。

可以将受约束的泛型参数用于方法返回后不会保留的内容。 不幸的是,它们有一个重大限制。 方法如下:

void foo(T param( where T:IFoo,IBar {...}

如果在编译时知道T实现IFooIBar的特定类型,则很容易调用。 不幸的是,很难构造一个对象集合,这些对象可以传递给像上面这样的例程,而它们都共享一个实现两个接口的公共基类型。 如果一个人想尝试持有一个集合"同时实现IFooIBar的东西,可以传递给期望相同的例程",有办法做到这一点,但它们并不容易。

如果可以预先确定可能想要传递给定对象的所有例程,则可以让集合在给定对象时为每个对象创建封闭的泛型委托。 在可行的情况下,这将起作用,并且可能相当有效。 但是,它确实在集合类和集合中的对象的使用之间引入了非常强的依赖关系。

一种更通用的方法是让集合保存包装器对象,这些对象支持对其内部对象执行任意约束开放泛型操作的方法。 遗憾的是,委托不能很好地用于此工作,因为 .net 不提供任何机制来调用开放泛型委托,而无需首先使用反射将它们转换为封闭形式。 相反,必须定义如下接口:

interface IActUpon<T,U> {  void DoSomething(ref MT it( 其中 MT:T,U;}interface IActUpon<T,U,PT1> {  void DoSomething(ref MT it, ref PT1 p1( 其中 MT:T,U;}接口 IActUpon<T,U,PT1,PT2> {  void DoSomething(ref MT it, ref PT1 p1, ref PT2 p2( 其中 MT:T,U;}

如果对象支持方法系列方法:

void DoSomethingWithMe(IActUpon<T,U> proc(;void DoSomethingWithMe(IActUpon<T,U,PT1> proc, ref PT1 p1(;void DoSomethingWithMe(IActUpon<T,U,PT1,PT2> proc, ref PT1 p1, ref PT2 p2(;

可以通过创建一个实现IActUpon<T,U,...>的适当变体的简单类,让对象调用任何需要约束为 T 和 U 类型的参数的所需例程。

不幸的是,虽然这种方法几乎是通用的(最大的限制是必须为任何特定数量的泛型参数显式编码(,但它充其量是尴尬的。 有一些编译器支持,它可能不是太糟糕,但除此之外,对于一般使用来说可能太难听了。