相关文章:C# interface method ambiguity
来自同一来源的代码:
private interface IBase1
{
int Percentage { get; set; }
}
private interface IBase2
{
int Percentage { get; set; }
}
private interface IAllYourBase : IBase1, IBase2
{
}
private class AllYourBase : IAllYourBase
{
private int _percentage;
public int Percentage
{
get { return _percentage; }
set { _percentage = value; }
}
}
private void Foo()
{
IAllYourBase iayb = new AllYourBase();
int percentage = iayb.Percentage; // Fails to compile. Ambiguity between 'Percentage' property.
}
(但不回答我的问题 - “为什么合同变得含糊不清?”)
假设:
接口是实施类必须遵守的合同。
如果两个(或更多)接口请求相同的合同,并且接口将它们传递给“forward”,然后类实现它们和ACCEPTS,那么普通合同应该只用作实现类的一个合同(通过不提供一个明确的实现)。然后,
为什么编译器会对共同合同显示“歧义”警告?
为什么编译器在尝试通过接口(iayb.Percentage)访问不明确的合同时无法编译?
我想知道编译器使用此限制有什么好处?
编辑:提供一个真实世界的用例,我希望跨接口使用合同作为一个合同。
public interface IIndexPriceTable{
int TradeId{get;}
int IndexId{get;}
double Price{get;}
}
public interface ILegPositionTable{
int TradeId {get;}
int LegId {get;}
int Position {get;}
}
public interface ITradeTable {
int TradeId{get;}
int IndexId{get;}
int LegId{get;}
//others
}
public interface IJoinedTableRecord : IIndexPriceTable, ILegPositionTable, ITradeTable {
//Just to put all contracts under one interface and use it as one concrete record, having all information across different tables.
}
答案 0 :(得分:13)
解决方案是使用new关键字再次定义属性Percentage:
private interface IBase1
{
int Percentage { get; set; }
}
private interface IBase2
{
int Percentage { get; set; }
}
private interface IAllYourBase : IBase1, IBase2
{
new int Percentage { get; set; }
}
private class AllYourBase : IAllYourBase
{
private int _percentage;
public int Percentage
{
get { return _percentage; }
set { _percentage = value; }
}
}
private void Foo()
{
IAllYourBase iayb = new AllYourBase();
int percentage = iayb.Percentage; //OK
}
注意:
接口的C#方法与方法plan by Bjarne StrouStrup in C++14非常不同。在C#中,您必须声明,类通过修改类本身来实现接口,而在C ++ 14中,它只需要具有与接口定义相对应的方法。因此,C#中的代码具有更多的依赖关系,这些依赖关系在C ++ 14中编码。
答案 1 :(得分:3)
因为编译器无法确定您尝试访问的基本接口实现(IBase1.Percentage
或IBase2.Percentage
),因为您的IAllYourBase
接口需要在之后< / em>他们和两者都有他们自己的 Percentage
属性。
这样说:只是因为两个接口具有相同名称和类型的属性并不意味着该属性在两个接口中的工作方式相同。即使公共接口继承自具有相同成员的两个接口,编译器也不能将两个看似相同的属性合并为一个,因为它们是两个不同合同的成员。
答案 2 :(得分:3)
因为接口IAllYourBase
没有声明Percentage
属性本身。
当您将AllYourBase
的实例分配给变量IAllYourBase
时,编译器需要输出对IBase1.Percentage
或IBase2.Percentage
的调用:
callvirt instance int32 IBase1::get_Percentage()
或
callvirt instance int32 IBase2::get_Percentage()
这些是不同类型的不同成员,只是因为它们具有相同的签名并不意味着它们可以互换。
在您的实际情况中,您可能需要更精细的粒度接口来定义公共属性。
答案 3 :(得分:1)
行int percentage = iayb.Percentage;
不知道它正在处理AllYourBase
类,只要不管它是什么,它都会实现IAllYourBase
接口。
假设我尝试使用DoubleBase
类执行相同的语句:
private class DoubleBase : IAllYourBase
{
int IBase1.Percentage { get; set; } = 10;
int IBase2.Percentage { get; set; } = 20;
}
int percentage
设置的值是什么?
答案 4 :(得分:0)
我明白你的观点。我想这个编译器限制的主要好处是最好有一个,然后不是。即那么你的无意的界面clush会被忽略,然后从这个奇怪的案例中受益(如果有的话)你会想要这样的行为。
BTW任何真实世界的场景中,期望的行为都会非常有用吗?
答案 5 :(得分:0)
如果一个接口继承了另外两个具有同名成员的接口,那么必须应用以下两个条件之一:
如果一个人不做其中的一件事,那么编译器最好不要试图猜测。假设有一个接口IListOfDigits
,其Add
方法将0-9整数添加到列表中,IBigNumber
,其Add
方法以算术方式添加数字。一个还有一个继承IListOfDigitsRepresentingBigNumber的接口。给定IListOfDigitsRepresentingBigNumber
名为myThing
,保持数字“5,4,3,2”,myThing.Add(1)的效果应该是什么?它应该改变myThing
以保持“5,4,3,2,1”(IListOfDigits.Add
)或“5,4,3,3”的效果(IBigNumber.Add
的效果)?如果执行上述任一操作,编译器将毫无困难地确定要使用哪种Add
方法。否则,如果两种方法都可以接受int
,则不会有线索。
顺便说一句,泛型和重载是一个有趣的案例。如果IFoo<T,U>
有成员void Bar(T param)
和void Bar(U param)
,则无法将类声明为实施IFoo<int,int>
。另一方面,可以将类Foo<T,U>
声明为实现IFoo<T,U>
,然后将其他类声明为继承自Foo<int,int>
,因为即使T
和{{1引用相同的类型,编译器仍会使用U
和T
来解决重载。