考虑以下示例:
interface IBase1
{
int Percentage { get; set; }
}
interface IBase2
{
int Percentage { get; set; }
}
interface IAllYourBase : IBase1, IBase2
{
}
class AllYourBase : IAllYourBase
{
int percentage;
int Percentage {
get { return percentage; }
set { percentage = value; }
}
}
void Foo()
{
IAllYourBase iayb = new AllYourBase();
int percentage = iayb.Percentage; // Fails to compile. Ambiguity between 'Percentage' property.
}
在上面的示例中,要调用哪个
Percentage
属性之间存在歧义。假设 IBase1
和 IBase2
接口可能不 被更改,我将如何以最干净、最优选的方式解决这种歧义?
根据我使用显式接口实现得到的答复,我想提一下,虽然这确实解决了问题,但它并没有以理想的方式解决它,因为我使用我的
AllYourBase
对象作为 IAllYourBase
大多数的时间,从来没有作为 IBase1
或 IBase2
。这主要是因为 IAllYourBase
还具有由 AllYourBase
实现的接口方法(我未能在上面的代码片段中详细说明这些方法,因为我认为它们不相关),并且我也想访问这些方法。一直来回转换会变得非常乏味并导致代码混乱。
我确实尝试了一种解决方案,涉及在
Percentage
中定义 IAllYourBase
属性,并且不使用显式接口实现,这似乎至少消除了编译器错误:
class IAllYourBase : IBase1, IBase2
{
int Percentage { get; set; }
}
这是一个有效的解决方案吗?
明确实施:
public class AllYourBase : IBase1, IBase2
{
int IBase1.Percentage { get{ return 12; } }
int IBase2.Percentage { get{ return 34; } }
}
如果你这样做,你当然可以像平常一样对待你的非模糊属性。
IAllYourBase ab = new AllYourBase();
ab.SomeValue = 1234;
但是,如果你想访问百分比属性,这将不起作用(假设它确实如此,预计返回哪个值?)
int percent = ab.Percentage; // Will not work.
您需要指定返回哪个百分比。这是通过转换到正确的接口来完成的:
int b1Percent = ((IBase1)ab).Percentage;
正如你所说,你可以在界面中重新定义属性:
interface IAllYourBase : IBase1, IBase2
{
int B1Percentage{ get; }
int B2Percentage{ get; }
}
class AllYourBase : IAllYourBase
{
public int B1Percentage{ get{ return 12; } }
public int B2Percentage{ get{ return 34; } }
IBase1.Percentage { get { return B1Percentage; } }
IBase2.Percentage { get { return B2Percentage; } }
}
现在您已经通过不同的名称解决了歧义。
假设您希望两个属性都访问 percent 成员变量,您可以使用显式接口实现并扩展 IAllYouBase 接口来完成此操作:
interface IAllYourBase : IBase1, IBase2
{
new int Percentage { get; set; }
}
class AllYourBase : IAllYourBase
{
int percentage;
public int Percentage {
get { return percentage; }
set { percentage = value; }
}
int IBase1.Percentage {
get { return percentage; }
set { percentage = value; }
}
int IBase2.Percentage {
get { return percentage; }
set { percentage = value; }
}
}
它并不漂亮,但它会给你我认为你想要的行为。
显式实现接口。
显式接口成员实现是引用完全限定的接口成员名称
的方法、属性、事件或索引器声明
请参阅 此 MSDN 页面,了解详细教程。
interface AllYourBase : IBase1, IBase2
{
int IBase1.Percentage { get; set; }
int IBase2.Percentage { get; set; }
}
尽管您已经接受了答案。我认为显式实现需要太多冗余工作(在您的情况下实现相同的属性两次)!
进一步隔离接口怎么样(Interface Segregation Principle)?
internal interface IPercentage
{
int Percentage { get; set; }
}
internal interface IBase1 : IPercentage
{
}
internal interface IBase2 : IPercentage
{
}
internal interface IAllYourBase : IBase1, IBase2
{
}
internal class AllYourBase : IAllYourBase
{
private int percentage;
public int Percentage
{
get { return percentage; }
set { percentage = value; }
}
void Foo()
{
IAllYourBase iayb = new AllYourBase();
int percentage = iayb.Percentage; // Compiles now!!!
}
}
显式实现并访问它:
interface IAllYourBase : IBase1, IBase2 { }
public class AllYourBase : IAllYourBase
{
int IBase1.Percentage { get{ return 12; } }
int IBase2.Percentage { get{ return 34; } }
}
IAllYourBase base = new AllYourBase();
int percentageBase1 = (base as IBase1).Percentage;
int percentageBase2 = (base as IBase2).Percentage;
如果您实际上不需要为
Percentage
属性返回不同的值,则可以通过分别从每个接口(而不是“主”接口)“派生”来消除编译器错误:
public class AllYourBase : IBase1, IBase2
{
// No need to explicitly implement if the value can be the same
public double Percentage { get { return 12d; } }
}
当然,如果您确实需要单独的值,那么您必须显式实现接口,并且通过适当类型的引用访问属性:
public class AllYourBase : IBase1, IBase2
{
// No need to explicitly implement if the value can be the same
public double IBase1.Percentage { get { return 12d; } }
public double IBase2.Percentage { get { return 34d; } }
}
代码:
public void SomeMethod()
{
AllYourBase ayb = new AllYourBase();
IBase1 b1 = ayb
double p1 = b1.Percentage;
IBase2 b2 = ayb;
double p2 = b2.Percentage;
}
显式实现接口时的一个重要考虑因素是
AllYourBase
本身不再具有Percentage
属性。仅当通过类型为接口之一的引用访问对象时才能访问它:
public void SomeMethod()
{
AllYourBase ayb = new AllYourBase();
double d = ayb.Percentage; // This is not legal
}
更新:看看您的编辑,您的解决方案很好,假设您不需要
IBase1
和 IBase2
的不同行为。您的解决方案隐藏了这些属性,因此只能通过将对象转换为这两个接口之一来访问它们。
您可以在
IAllYourBase
界面中定义属性。
类似这样的事情:
interface IAllYourBase : IBase1, IBase2
{
new int Percentage { get; set; }
}
这将解决属性之间的歧义问题。并且您可以保留界面的结构。
虽然这些人提到的显式实现显然是正确的,但为了理智起见,请考虑以下场景(或者只是为了将来阅读你的代码的人这样做):
如果这些百分比根据界面的不同而具有不同的含义,为什么不给它们一些有意义的名称呢?
interface IBase1
{
int PercentageBase1 { get; set; }
}
interface IBase2
{
int PercentageBase2 { get; set; }
}
另一方面,如果它们具有相同的含义,为什么不在一个界面中只使用一个百分比并从另一个界面派生一个百分比?
interface IBase1
{
int Percentage { get; set; }
}
interface IBase2 : IBase1
{
}
但是,如果最后一个操作由于某种原因而无法实现,请考虑为它们创建一个包含该属性的基本接口:
interface IBase0
{
int Percentage { get; set; }
}
interface IBase1 : IBase0
{
}
interface IBase2 : IBase0
{
}
void Foo()
{
IAllYourBase base = new AllYourBase();
int percentage = base.Percentage; // Fails to compile. Ambiguity between 'Percentage' property.
}
在此示例中,您使用
base
关键字作为对象名称,这可能会导致问题!!!
我来这里是因为我在使用通用接口时遇到了这个问题,例如:
public interface IValue1<T>
{
T Value { get; }
}
public interface IValue2<T>
{
T Value { get; }
}
public interface IValue<T> : IValue1<T>, IValue2<T>
{
}
由于 C# 允许在接口中进行一些实现,所以我这样解决了:
public interface IValue1<T>
{
T Value { get; }
}
public interface IValue2<T>
{
T Value { get; }
}
public interface IValue<T> : IValue1<T>, IValue2<T>
{
new T Value { get; } // Hides both IValue1 and IValue2 Value properties.
// Self implement/resolve both interfaces value.
T IValue1<T>.Value => Value;
T IValue2<T>.Value => Value;
}
所以我只需要实现
IValue
:
public class Value : IValue<int>
{
public int Value => 42;
}
这个答案中建议的另一种方法是在两者之间共享更高的基本接口,但这不适用于我的情况。