Подтвердить что ты не робот

Нечеткость метода интерфейса С#

Рассмотрим следующий пример:

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 не могут быть изменены, как я могу решить эту двусмысленность самым чистым, наиболее предпочтительным способом?

Update

Основываясь на ответах, которые я получал для использования явной реализации интерфейса, я хочу упомянуть, что, хотя это и решает проблему, он не идеально подходит для меня, потому что я использую свой объект AllYourBase как IAllYourBase большую часть времени, а не как IBase1 или IBase2. Это связано главным образом с тем, что IAllYourBase также имеет методы интерфейса (я не смог подробно описать их в своем фрагменте кода выше, потому что я думал, что они неактуальны), которые реализованы с помощью AllYourBase, и я тоже хочу получить к ним доступ. Кастинг взад и вперед все время будет очень утомительным и приведет к запутанному коду.

Я попробовал одно решение, которое включало определение свойства Percentage в IAllYourBase и не используя явную реализацию интерфейса, которая, по-видимому, избавлялась от ошибки компилятора:

class IAllYourBase : IBase1, IBase2
{
   int Percentage { get; set; }
}

Является ли это допустимым решением?

4b9b3361

Ответ 1

Реализовать явно:

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; } }
}

Теперь вы решили двусмысленность разными именами.

Ответ 2

Внедрить интерфейсы явно.

Явная реализация члена интерфейса - это объявление метода, свойства, события или индексатора, которое ссылается на имя полностью идентифицированного имени интерфейса

См. эту страницу MSDN для подробного руководства.

interface AllYourBase : IBase1, IBase2
{
   int IBase1.Percentage { get; set; }
   int IBase2.Percentage { get; set; }
}

Ответ 3

Предполагая, что вы хотите, чтобы оба свойства получили доступ к переменной-члену %, вы можете выполнить это с помощью явной реализации интерфейса и расширения интерфейса 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; }
   }
}

Это не очень, но это даст вам поведение, о котором я думаю, что вы после.

Ответ 4

Явно реализовать и получить к нему доступ:

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

Если вам действительно не нужно возвращать разные значения для свойства 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. Ваше решение скрывает эти свойства, поэтому они будут доступны только путем переноса объекта на один из этих двух интерфейсов.

Ответ 6

Хотя вы уже приняли ответ. Я бы сказал, что явная реализация требует слишком большого количества избыточной работы (повторное использование того же свойства в вашем случае)!

Как насчет дальнейшей сегрегации интерфейса (принцип цельной цепочки)?

  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

В то время как явная реализация, о которой говорили ребята, явно правильна, рассмотрите следующие сценарии только ради здравомыслия (или сделайте это только для человека, читающего ваш код в будущем):

Если эти проценты имеют разные значения в зависимости от интерфейса, почему бы не дать им несколько значимых имен?

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
{
}

Ответ 8

void Foo()
{
   IAllYourBase base = new AllYourBase();
   int percentage = base.Percentage; // Fails to compile. Ambiguity between 'Percentage' property.
}

В этом примере вы используете ключевое слово base для имени объекта и которое может вызвать проблему!!!

Ответ 9

Вы можете определить свойство в интерфейсе IAllYourBase.

Что-то вроде этого:

interface IAllYourBase : IBase1, IBase2
{
   new int Percentage { get; set; }
}

Это позволит решить проблемы с неопределенностью между свойствами. И вы можете сохранить структуру интерфейсов.