Как наследовать метод, но с другим типом возврата? - PullRequest
9 голосов
/ 12 ноября 2009

Даны следующие классы:

ClassA
{
     public ClassA DoSomethingAndReturnNewObject()
     {}    
}

ClassB : ClassA
{}

ClassC : ClassA
{}

Есть ли способ заставить ClassB и ClassC наследовать метод, но настроить тип возвращаемого значения для их собственного класса?

Я предпочитаю не копировать метод из ClassA и не менять там тип.

Мне нужно получить ClassB объект, когда я звоню ClassB.DoSomethingAndReturnNewObject().

Мне нужно получить ClassC объект, когда я звоню ClassC.DoSomethingAndReturnNewObject().

Что-то вроде вызова конструктора на основе текущего типа, например: this.GetType()? Но я понятия не имею, как на самом деле это сделать.

Ответы [ 10 ]

8 голосов
/ 12 ноября 2009

Нет, упомянутая вами функция называется ковариация возвращаемого типа . Это не поддерживается в C #.

6 голосов
/ 12 ноября 2009

Вам необходимо создать защищенный виртуальный метод для использования DoSomethingAndReturnNewObject:

class ClassA
{
    protected virtual ClassA Create()
    {
        return new ClassA()
    }

    public ClassA DoSomethingAndReturnNewObject()
    {
        ClassA result = Create();
        // Do stuff to result
        return result;
    }
}

class ClassB : ClassA
{
     protected override ClassA Create() { return new ClassB(); }
}

class ClassC : ClassA
{
     protected override ClassA Create() { return new ClassC(); }
}

Обратите внимание, что тип возвращаемого значения остается ClassA, но тип экземпляра объекта будет определенным классом.

6 голосов
/ 12 ноября 2009

То, что вы описываете, является ковариантным типом возврата и не поддерживается в C #.

Тем не менее, вы можете создать ClassA как открытый универсальный тип, и закрытые универсальные наследники возвращают свой собственный тип.

Пример:

public abstract class ClassA<T> where T: ClassA<T>, new()
{
    public abstract T DoSomethingAndReturnNewObject();
}

public class ClassB: ClassA<ClassB>
{
    public override ClassB DoSomethingAndReturnNewObject()
    {
        //do whatever
    }
}
2 голосов
/ 12 ноября 2009
class ClassA<T> where T : ClassA<T>, new()
{
    public T DoSomethingAndReturnNewObject()
    {
        return new T();
    }
}

class ClassB : ClassA<ClassB> { }

class ClassC : ClassA<ClassC> { }

Тест:

ClassB b1 = new ClassB();

ClassB b2 = b1.DoSomethingAndReturnNewObject(); // returns instance of ClassB
0 голосов
/ 06 ноября 2013

На самом деле есть один простой ответ: заставить все методы возвращать типы "объект". Вы, очевидно, возвращаете разные типы, строки, целые и т. Д., И они останутся такими, какие они есть, после того, как достигнут своей цели. Но компилятор совершенно счастлив - все является «объектом». Вы отказываетесь от проверки типов во время компиляции, но это не очень хорошая вещь, но время от времени для глубокого программирования на ОО результат того стоит.

0 голосов
/ 12 ноября 2009

Я наконец разработал следующее решение. Для моего случая это достаточно полезно, но предполагает, что ClassA знает о своих производных и ограничен этими двумя вариантами. Не очень высокий уровень мышления, но это работает. :)

ClassA
{
    public ClassA DoSomethingAndReturnNewObject()
    {
        if (this.GetType() == typeOf(ClassB))
        {
            return new ClassB(values);
        }
        else
        {
            return new ClassC(values):
        }
    }    
}

ClassB : ClassA
{}

ClassC : ClassA
{}
0 голосов
/ 12 ноября 2009

Это не наследование, потому что тип возвращаемого значения метода является частью его сигнатуры. Вы не меняете метод, вы создаете совершенно новый.

У вас есть несколько вариантов. Например, вы можете сделать метод DoSomethingAndReturnNewObject универсальным и вернуть его универсальный тип. Это, вероятно, самый прямой путь к точному поведению, которое вы ищете.

Другая альтернатива - оставить сигнатуры методов без изменений, а методы подкласса возвращают экземпляры ClassB и ClassC. Тогда клиентский код должен отвечать за приведение, чтобы использовать эти объекты в качестве соответствующих производных классов.

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

0 голосов
/ 12 ноября 2009

Ну, правильный ответ - нет, и, как правило, это плохая идея. Если вы возвращаете что-то совершенно другое, найдите другой способ.

Однако, если вы не возвращаете что-то совершенно другое, интерфейс может решить вашу проблему. Вместо того, чтобы возвращать класс, возвращайте интерфейс, и классы A, B и C возвращают объекты, которые реализуют этот интерфейс так, как им удобно.

0 голосов
/ 12 ноября 2009

Может быть, дженерики будут вашим спасителем. Взгляните на эту ссылку: C # Generics Part 3/4: Методы приведения, наследования и универсальные

bstract class  B<T> {
   public abstract T Fct(T t);
}
class D1 : B<string>{
   public override string Fct( string t ) { return "hello"; }
}
class D2<T> : B<T>{
   public override T Fct(T t) { return default (T); }
}
0 голосов
/ 12 ноября 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...