4

假设我想表达“一种期望实现接口 IFoo 和 IBar 的对象的方法”。就像是:

void Method(IFoo+IBar param);

我怎么能用 C# 说这个?

(有语法结构吗?或者是一个好的习语?)


一次尝试

介绍一个接口:

interface IFooBar : IFoo, IBar {}

void Method(IFooBar param);

这很糟糕,我什至后悔想到它:-) 乍一看还不错,但可悲的是,它默默地引入了一个不应该存在的依赖项。

存在的唯一原因IFooBar是成为Method's 接口的一部分。因此,任何要用作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

4

4 回答 4

9
public void someMethod<T>(T param) where T : IFoo, IBar
{...}
于 2012-04-27T13:54:30.180 回答
2

我的一个朋友曾经问 Eric Lippert 一个非常相似的问题,他的回答是这样的:

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

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

问题比声明变量更大。本质上,您是说要将变量的约束从“变量必须是给定类型”更改为“变量必须是多个给定类型”。但为什么要停留在变量上呢?如果那是变量的类型约束,那么它应该是一个类型,句号。你应该可以说:

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

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

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

我自己也经常想要这个功能。我同意这是一个不错的功能,但我认为现在添加它太昂贵了。下次您设计新类型系统时,请从头开始包含该功能!

于 2012-04-27T13:55:17.683 回答
2

您可以通过如下定义您的方法来实现它

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

希望这可以帮助。

于 2012-04-27T13:57:13.610 回答
1

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

void foo(T param) 其中 T:IFoo,IBar {...}

如果在编译时知道这T某种特定类型,它实现了IFoo和,则很容易调用IBar。不幸的是,如果它们不共享一个实现两个接口的公共基类型,就很难构造一个可以传递给上述例程的对象集合。如果一个人想尝试保存一组“实现IFoo和的东西IBar,并且可以传递给期望相同的例程”,有很多方法可以做到,但它们并不容易。

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

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

接口 IActUpon<T,U> {
  void DoSomething<MT>(ref MT it) where MT:T,U;
}
接口 IActUpon<T,U,PT1> {
  void DoSomething<MT>(ref MT it, ref PT1 p1) where MT:T,U;
}
接口 IActUpon<T,U,PT1,PT2> {
  void DoSomething<MT>(ref MT it, ref PT1 p1, ref PT2 p2) where 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,...>.

不幸的是,虽然这种方法几乎是通用的(最大的限制是必须为任何特定数量的通用参数显式编码),但它充其量是尴尬的。有了一些编译器支持,它可能不会太糟糕,但否则它对于一般用途来说可能太恶心了。

于 2012-04-27T15:39:01.347 回答