В C# метод интерфейса двусмысленности
рассмотрим следующий пример:
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; }
}
это верное решение?
9 ответов
явно реализовать:
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; } }
}
теперь вы разрешили двусмысленность различными именами вместо этого.
явной реализации интерфейсов.
реализация явного члена интерфейса-это объявление метода, свойства, события или индексатора, ссылающееся на полное имя интерфейса
посмотреть эта страница MSDN подробное руководство.
interface AllYourBase : IBase1, IBase2
{
int IBase1.Percentage { get; set; }
int IBase2.Percentage { get; set; }
}
предполагая, что вы хотите, чтобы оба свойства имели доступ к процентов переменная-член Вы можете выполнить это, используя явную реализацию интерфейса и расширение 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; }
}
}
это не очень красиво, но это даст вам поведение, которое, я думаю, вам нужно.
явно реализовать и получить к нему доступ:
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
свойство, вы можете устранить ошибку компилятора, "выводя" из каждого интерфейса отдельно, а не из интерфейса "master":
public class AllYourBase : IBase1, IBase2
{
// No need to explicitly implement if the value can be the same
public double Percentage { get { return 12d; } }
}
конечно, если вы do нужны отдельные значения, тогда вам придется явно реализовать интерфейсы,и доступ к свойству с помощью соответствующего типа ссылки:
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;
}
одним из важных соображений при реализации интерфейсов явно является то, что больше нет Percentage
свойства. Он может быть доступен только тогда, когда объект доступен через ссылку, введенную как один из интерфейсов:
public void SomeMethod()
{
AllYourBase ayb = new AllYourBase();
double d = ayb.Percentage; // This is not legal
}
обновление: глядя на ваше редактирование, ваше решение в порядке, предполагая, что вам не нужно другое поведение для IBase1
и IBase2
. Ваше решение скрывает эти свойства, поэтому они будет осуществляться только путем приведения объекта к одному из этих двух интерфейсов.
хотя вы уже приняли ответ. Я бы сказал, что явная реализация требует слишком много избыточной работы (реализация одного и того же свойства дважды, в вашем случае) !
Как насчет дальнейшей сегрегации интерфейса (принцип сегрегации Inteface) ?
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 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
ключевое слово для имени объекта, и это может вызвать проблему!!!
Вы можете определить свойство в IAllYourBase
интерфейс.
что-то вроде этого :
interface IAllYourBase : IBase1, IBase2
{
new int Percentage { get; set; }
}
это решит проблемы с неоднозначностью между свойствами. И вы можете сохранить структуру интерфейсов.