可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Consider the following example:
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.
}
In the example above, there is ambiguity between which Percentage
property to call. Assuming the IBase1
and IBase2
interfaces may not be changed, how would I go about resolving this ambiguity in the cleanest, most preferred way?
Update
Based on the responses I was getting for using explicit interface implementation, I want to mention that while this does solve the problem it does not solve it in an ideal way for me because I use my AllYourBase
object as an IAllYourBase
most of the time, never as an IBase1
or IBase2
. This is mostly because IAllYourBase
also has interface methods (I failed to detail those in my code snippet above because I thought they were irrelevant) that are implemented by AllYourBase
and I want to access those too. Casting back and forth all the time will get very tedious and result in messy code.
I did try one solution that involved defining the Percentage
property in IAllYourBase
and not using explicit interface implementation, which seemed to get rid of the compiler error at least:
class IAllYourBase : IBase1, IBase2
{
int Percentage { get; set; }
}
Is this a valid solution?
回答1:
Implement explicitly:
public class AllYourBase : IBase1, IBase2
{
int IBase1.Percentage { get{ return 12; } }
int IBase2.Percentage { get{ return 34; } }
}
If you do this, you can of course treat your non-ambigous properties just like normal.
IAllYourBase ab = new AllYourBase();
ab.SomeValue = 1234;
However, if you want to access the percentage prop this will not work (Suppose it did, which value would be expected in return?)
int percent = ab.Percentage; // Will not work.
you need to specify which percentage to return. And this is done by casting to the correct interface:
int b1Percent = ((IBase1)ab).Percentage;
As you say, you can redefine the properties in the interface:
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; } }
}
Now you have resolved the ambiguity by distinct names instead.
回答2:
Implement the interfaces explicitly.
An explicit interface member implementation is a method, property, event, or indexer declaration that references a fully qualified interface member name
See this MSDN page for a detailed tutorial.
interface AllYourBase : IBase1, IBase2
{
int IBase1.Percentage { get; set; }
int IBase2.Percentage { get; set; }
}
回答3:
Assuming that you want both properties to access the percent member variable you can accomplish this using explicit interface implementation and extending the IAllYouBase interface:
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; }
}
}
It's not pretty, but it will give you the behavior I think you're after.
回答4:
Explicitly implement and access it:
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;
回答5:
If you don't actually need to return different values for the Percentage
property, you can eliminate the compiler error by "deriving" from each interface separately, rather than the "master" interface:
public class AllYourBase : IBase1, IBase2
{
// No need to explicitly implement if the value can be the same
public double Percentage { get { return 12d; } }
}
Of course if you do need separate values, then you'll have to explicitly implement the interfaces, and access the property via an appropriately typed reference:
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; } }
}
And the code:
public void SomeMethod()
{
AllYourBase ayb = new AllYourBase();
IBase1 b1 = ayb
double p1 = b1.Percentage;
IBase2 b2 = ayb;
double p2 = b2.Percentage;
}
One important consideration when implementing the interfaces explicitly is that AllYourBase
itself no longer has a Percentage
property. It can only be accessed when the object is accessed via a reference typed as one of the interfaces:
public void SomeMethod()
{
AllYourBase ayb = new AllYourBase();
double d = ayb.Percentage; // This is not legal
}
UPDATE: Looking at your edit, your solution is fine, assuming you don't need different behaviour for IBase1
and IBase2
. Your solution hides those properties, so they will only be accessible by casting the object to one of those two interfaces.
回答6:
Though you have already accepted the answer. I would argue that explicit implementation asks too much of redundant work (Implementing the same property twice, in your case) !
How about further segregation of interface (Inteface 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!!!
}
}
回答7:
While explicit implementation the guys have mentioned is obviously correct, please consider following scenarions just for the sake of sanity (or do it just for a person reading your code in the future):
If these percentages have different meanings depending on the interface, why not to give them some meaningful names?
interface IBase1
{
int PercentageBase1 { get; set; }
}
interface IBase2
{
int PercentageBase2 { get; set; }
}
And, on the other hand, if they have the same meaning, why not to have just one Percentage in one of the interfaces and just derive one from another?
interface IBase1
{
int Percentage { get; set; }
}
interface IBase2 : IBase1
{
}
However, if the last operation is not possible for whatever the reason is, please consider creating a base interface containing that property for both of them:
interface IBase0
{
int Percentage { get; set; }
}
interface IBase1 : IBase0
{
}
interface IBase2 : IBase0
{
}
回答8:
void Foo()
{
IAllYourBase base = new AllYourBase();
int percentage = base.Percentage; // Fails to compile. Ambiguity between 'Percentage' property.
}
In this example you use the base
keyword for an object name and that may be causing the problem!!!
回答9:
You can define the property in the IAllYourBase
interface.
Something like this :
interface IAllYourBase : IBase1, IBase2
{
new int Percentage { get; set; }
}
This will resolve the problems with the ambiguity between the properties. And you can keep the structure of the interfaces.