C # — Использование подклассов для реализации интерфейсов

#c# #inheritance #interface

#c# #наследование #интерфейс

Вопрос:

У меня есть следующие интерфейсы

     public interface IPrice
    {
        int Price { get; }
    }

    public interface IGear : IPrice
    {
        GearUpgrade Upgrades { get; }
    }
  

и следующие классы

     public class GearUpgrade : IPrice
    {
        public int Price
        {
            get { return price; }
            set { price = value; }

        }
    }

    public class ArmorUpgrade : GearUpgrade
    {
    }

    public class ShieldUpgrade : GearUpgrade
    {
    }

    public class WeaponUpgrade : GearUpgrade
    {
    }
  

Итак, когда я пытаюсь реализовать IGear, подобный этому…

     public class Armor : IGear
    {
        private int price = 0;
        public int Price { get => price; }

        private ArmorUpgrade upgrades;
        public ArmorUpgrade Upgrades
        {
            get { return upgrades; }
            set { upgrades = value; }
        }

    }
  

Я получаю следующую ошибку:

‘Armor’ не реализует элемент интерфейса ‘IGear.Upgrades’. ‘Броня.Обновления’ не могут реализовать ‘IGear.Upgrades’, потому что у него нет соответствующего возвращаемого типа ‘GearUpgrade’.

Я подумал, что если Upgrades это из подкласса GearUpgrade , интерфейс должен быть выполнен, но, по-видимому, это не так… Я сделал ложное предположение?

Комментарии:

1. Похоже, вы хотите IGear быть универсальным.

2. Не уверен, что бы вы это сказали. У меня есть занятие Armor , Weapon и Shield . Я решил, что интерфейсы были бы хорошей абстракцией для этого. Не могли бы вы порекомендовать мне другую реализацию и ее преимущества?

3. Это может быть связано с тем, что используемый пример не является реальным кодом, но в случае, если это так: механизм, безусловно, не имеет цены, в любом случае, механизм имеет цену

4. @EnriqueMorenoTent потому что похоже на то, что вы запрашиваете, поскольку код в вопросе показывает разные возвращаемые типы… что может быть прекрасно выполнено с помощью дженериков. Действительно, это вряд ли полезно для того, чего вы на самом деле пытаетесь достичь, но это вообще не вопрос.

5. для тех, кому не все равно, эта проблема заключается в том, что c # не имеет ковариантных возвращаемых типов. Есть предложение добавить это здесь infoq.com/news/2020/01/CSharp-Covariant-Return-Types

Ответ №1:

Для реализации интерфейса сигнатуры методов должны точно совпадать: C # не применяет ковариацию и контравариантность неявно.

Вот простое исправление, позволяющее Armor реализовать IGear интерфейс, не вмешиваясь в класс:

 public class Armor : IGear
{
    private int price = 0;
    public int Price { get => price; }

    private ArmorUpgrade upgrades;
    public ArmorUpgrade Upgrades
    {
        get { return upgrades; }
        set { upgrades = value; }
    }

    GearUpgrade IGear.Upgrades => this.Upgrades;
}
  

Комментарии:

1. Интересно. Это решение кажется более элегантным.

2. @EnriqueMorenoTent Ну, общий был бы намного элегантнее

3. @Franck: Это, вероятно, зависит от того, как много вы знаете о своем IGear , когда пытаетесь его использовать. Если все, что вы знаете, это то, что это «Снаряжение», то вы, вероятно, не собираетесь разыгрывать его как IGear<ArmorUpgrade> : в этот момент вы с таким же успехом разыгрываете его на Armor .