假设我想表达“一个期望实现接口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
。
答案 0 :(得分:9)
public void someMethod<T>(T param) where T : IFoo, IBar
{...}
答案 1 :(得分:2)
我的一位朋友曾经问过Eric Lippert一个非常类似的问题,这是他的回答:
问:C#是否有理由不允许声明变量 多个接口?
答:假设我们将其注释为{IFoo,IBar}。
问题大于声明变量。基本上你是 说你要改变对“变量”的约束 变量必须是给定类型“to”变量必须全部 几种给定类型“。但为什么要停止变量?如果是那种类型 变量的约束然后应该是一个类型,句点。您 应该可以说:
List< { IFoo, IBar } > myList; Or
public static { IFoo, IBar } MyMethod( { IFoo, IBar }[ ] foobars )
{ return foobars[0]; }
等等。中途完成这项功能毫无意义。
这将是类型系统的主要扩展。我们想要 支持每种托管语言,而不仅仅是C#和VB。这是 您希望在版本中进入框架的那种功能 一;这是非常昂贵的,很难做出如此重大的改变 当你有四个版本并拥有数十亿行客户时 代码必须继续工作。
我自己经常想要这个功能;我同意这很好 功能,但我认为现在添加它太昂贵了。下一个 你设计一个新类型系统的时间,包括该功能 开始!
答案 2 :(得分:2)
您可以通过定义下面的方法来实现它
void Method<T>(T param)
where T: IFoo, IBar
{
}
希望这有帮助。
答案 3 :(得分:1)
可以使用受约束的泛型参数来处理方法返回后不会持久化的事物。不幸的是,它们存在很大的局限性。一种方法如:
void foo(T param) where T:IFoo,IBar {...}如果在编译时知道
T
是某个特定类型,它实现IFoo
和IBar
,那么很容易调用。遗憾的是,很难构造一个对象集合,这些对象可以传递给上面的例程,而不需要它们共享一个实现两个接口的公共基类型。如果想要尝试持有一个集合
“同时实现IFoo
和IBar
的东西,并且可以传递给期望相同的例程”,有办法实现,但它们并不容易。
如果可以事先确定一个人可能希望传递给对象的所有例程,则可以让一个集合为每个给定的对象创建封闭的通用委托,在给出一个对象时。在可行的情况下,这将起作用并且可能是合理有效的。但是,它确实在集合类和集合中对象的使用之间引入了非常强的依赖关系。
一种更通用的方法是让集合保持包装器对象,该对象支持一种方法,以对其内部对象执行任意约束 - 开放 - 通用操作。不幸的是,代表不能很好地工作,因为.net没有提供任何机制来调用开放的泛型委托而不首先使用Reflection将它们转换为封闭形式。相反,必须定义如下的接口:
interface IActUpon<T,U> { void DoSomething<MT>(ref MT it) where MT:T,U; } interface IActUpon<T,U,PT1> { void DoSomething<MT>(ref MT it, ref PT1 p1) where MT:T,U; } interface 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,...>
的适当变体的简单类,可以让对象调用任何需要通过约束类型T和U的所需例程。
不幸的是,尽管这种方法几乎是一般性的(最大的限制是必须明确地为任何特定数量的通用参数编码)但它充其量只是尴尬。有了一些编译器的支持,它可能不会太糟糕,但是对于一般用途来说它可能太危险了。