Абстрактный метод, который возвращает экземпляр производного класса - PullRequest
9 голосов
/ 20 февраля 2012

Можно ли создать абстрактный метод, который должен возвращать экземпляр производного класса?Я могу сделать это:

abstract class Base
{
   public abstract Base GetObj();
}

class Derived : Base
{
   public Derived() { }

   public override Base GetObj()
   {
       return new Derived();
   }
}

Но мне было интересно, есть ли способ сделать это так, что Derived::GetObj() вынужден вернуть Derived?

Спасибо.

Ответы [ 2 ]

20 голосов
/ 20 февраля 2012

Использование обобщений должно сделать это возможным:

abstract class Base<T>
    where T : Base<T>
{
   public abstract T GetObj();
}

class Derived : Base <Derived>
{
   public Derived() { }

   public override Derived GetObj()
   {
       return new Derived();
   }
}

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

abstract class Base<T>
    where T : Base<T>, new()
{
    public static T GetObj()
    {
        return new T();
    }
}

class Derived : Base<Derived>
{
    public Derived() { }
}
7 голосов
/ 20 февраля 2012

То, что у вас есть, - почти, но не совсем абстрактная фабрика. Сначала я скажу, что вы должны оставить это на усмотрение разработчиков производных классов, чтобы сделать это правильно, или просто поверить, что они это сделают.

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

public abstract class Foo<T> where T : Foo<T>
public class Bar : Foo<Bar>

Эта идея может работать на других языках. Он работает в C #, только если люди используют его правильно. С приведенным выше определением Бар, теперь я также могу иметь

public class Baz : Foo<Bar> 

Что совершенно законно. Bar - это Foo<Bar>, и это все, что требуется Baz для его использования. Ничто не требует, чтобы Баз фактически использовал Foo<Baz>.

Система типов в C # просто не может принудительно применить то, что вы хотели бы применять. Даже с этим шаблоном вы находитесь в том же положении, что и раньше. Вы все еще должны доверять разработчикам производных классов, чтобы сделать это правильно.

Подробнее об этой теме вы можете прочитать этот блог .

...