Наследование .NET: Возвращение экземпляра класса, который реализует данный интерфейс из функции MusiInherit / abstract - PullRequest
1 голос
/ 14 ноября 2010

У меня есть базовый класс, который содержит метод abstract / MustInherit. Я хочу, чтобы этот метод возвращал экземпляр класса, который реализует данный интерфейс.

Когда я прихожу к реализации метода в производном классе, я получаю ошибку компилятора, потому что возвращаемые типы не совпадают (т. Е. В абстрактном классе возвращается тип интерфейса, тогда как в производном классе тип возвращаемого значения - это класс, который реализует интерфейс).

Может кто-нибудь прояснить для меня основную проблему? Я не могу понять, что не так. Особенно учитывая, что указанный тип возвращаемого значения абстрактного метода является интерфейсом, экземпляр которого не может быть создан, поэтому, безусловно, реализация метода должна возвращать экземпляр класса, который реализует интерфейс? Может быть, я просто не вижу дрова для деревьев.

Я могу закодировать проблему, сделав базовый класс универсальным (и я могу указать, что T должен реализовывать мой интерфейс), тогда абстрактный метод вернет экземпляр T. Хотя я не уверен насчет этого решения. Это похоже на хак, я не понимаю, почему я не могу сделать это с прямым наследованием.

Ответы [ 5 ]

2 голосов
/ 14 ноября 2010

Теперь я представляю ваш код так, что у вас есть абстрактный базовый класс, содержащий метод (GetFruit), который возвращает интерфейс, который я назову IFruit. В производном классе вы хотите иметь возможность возвращать (из этого самого метода GetFruit) объект Apple, который реализует интерфейс IFruit.

Однако, как бы это ни было удобно, Спецификация общего языка (CLS) не позволяет совместимым языкам поддерживать ковариацию возвращаемого типа . Это включает в себя все языки .NET, такие как VB.NET и C #. Существует много дезинформации о том, почему именно возникает это ограничение и что именно отвечает за это ограничение, но простой ответ заключается в том, что вы не можете сузить тип возвращаемого значения для метода в производном классе до более конкретного деривации.

Это означает, что, хотя Apple реализует IFruit, вы не можете набрать метод в производном классе, чтобы вернуть Apple. Переопределенные методы должны иметь такую ​​же сигнатуру возвращаемого значения, как и у их базы. Таким образом, вместо этого каждый метод должен возвращать объект того же типа (IFruit) для всех классов, производных от вашего базового класса. Просто изменив возвращаемое значение метода в Ваш производный класс того же типа, что и метод в базовом классе, решит вашу проблему.

Вы упомянули возможность сделать базовый класс универсальным, и, хотя это, вероятно, сработало бы, оно, похоже, создает ненужную сложность. Используя мой надуманный пример выше, поскольку Apple полностью реализует интерфейс IFruit, вы должны получить все преимущества проверки типов и даже Intellisense, просто возвращая IFruit из ваших производных классов.

РЕДАКТИРОВАТЬ: Если вы открыты для чего-то немного хакерского, вы всегда можете повторно объявить метод в вашем производном классе (пометив его как new или Shadows) , Это позволит вам вернуть любой тип объекта, который вы хотите, независимо от сигнатуры метода в базовом классе. Однако помните, что нет способа повторно объявить и переопределить метод одновременно, так что вы на самом деле не полиморфны.

1 голос
/ 14 ноября 2010

Вот один из способов добиться того, что вы хотите:

public interface ISomething {}
public class Something : ISomething {}

public abstract class Base
{
    public ISomething Method()
    {
        return DoMethod();
    }

    protected abstract ISomething DoMethod();
}

public class Derived : Base
{
    public new Something Method()
    {
        return (Something) DoMethod();
    }

    protected override ISomething DoMethod()
    {
        return null; //derived logic here
    }
}

Проблема заключается в том, что переопределенные методы должны иметь точно такую ​​же сигнатуру, что и их база. Предположим, вы должны создать тип значения, который реализует ISomething, а затем специализировать переопределение в Derived для возврата типа значения. Теперь переопределенный метод возвращает тип значения, который реализует интерфейс, но, вызывая метод из Base, вы ожидаете получить тип значения в штучной упаковке формы интерфейса. Если вызов Derived возвратил неупакованное значение при вызове через Base, вызывающий код попытается получить к нему доступ, как если бы он уже был в штучной упаковке, что привело к ошибке.

0 голосов
/ 14 ноября 2010

Вы не можете изменить тип возврата перегруженного метода в производном классе. Ни в C #, ни в C ++, ни в VB.NET. Оставьте все как есть, и вы можете вернуть экземпляр класса, который реализует интерфейс.

Причина: абстрактные методы предназначены для использования в абстрактных алгоритмах, которые работают только на общем интерфейсе различных производных классов. То, что вы описали, это просто случай абстрактной фабрики. Вы найдете подробное объяснение здесь:

http://en.wikipedia.org/wiki/Abstract_factory_pattern

0 голосов
/ 14 ноября 2010

Просто сделайте тип возврата вашего метода в производном классе интерфейсом.

Пример:

abstract class Base
{
    public abstract IInterface YourMethod();
}

class Derived
{
    public override IInterface YourMethod()
    {
        return new TypeThatImplementsIInterface();
    }
}
0 голосов
/ 14 ноября 2010

Из твоего вопроса кажется, что у тебя есть что-то вроде этого:

public abstract class Base
{
    public abstract ISomeInterface Get();
}

public class Sub : Base
{
    public override SomeInterfaceImpl Get() { .. }
}

Решение состоит в том, чтобы сохранить тип возвращаемого значения из базового класса, т. Е.

public override ISomeInterface Get()
{
    return new SomeInterfaceImpl();
}
...