Параметризованный класc. Возврат класса потомка

Рейтинг: 4Ответов: 1Опубликовано: 30.04.2015

Я так понимаю это сделать нельзя, но всё таки спрошу...

Есть у меня базовый абстрактный класс Control, где параметр T - это тоже какой-то класс Component, но это уже не так важно.

Я создаю потомка Button - наследник от Control.

public class MyButton : Control<SomeComponent>

В базовом классе у меня есть методы, они что-то делают и возвращают ссылку на самого себя. Бывает удобно сделать:

new MyButton().Text("Foo").Name("Boo")

Но проблема в том, я могу вернуть только Control в базовом классе. В итоге у меня получается:

// 1.
MyButton btn = new MyButton();
btn.Text("");

// 2.
Control<Button> btn = new MyButton().Text("");

А хотелось бы, чтобы в случае варианта 2 также возвращался MyButton.

Ответы

▲ 3Принят

Нормально это не поддерживается. Но есть костыль, который работает на одноуровневых иерархиях:

class Control<TComponent, TSelf>
    where TSelf : Control<TComponent, TSelf>
{
    public virtual TSelf Foo () { return (TSelf)this; }
    // ...
}

class Button : Control<Object, Button>
{
    public override Button Foo () { return this; }
    // ...
}

Если ниже кнопки по иерархии есть что-то ещё, то костыль уже не работает, к сожалению.

Как вариант, можете всегда перекрывать методы с помощью new:

class Control<TComponent>
{
    protected virtual Control<TComponent> FooImpl () { return this; }
    public Control<TComponent> Foo () { return FooImpl(); }
    // ...
}

class Button : Control<Object>
{
    protected override Control<Object> FooImpl () { return this; }
    public new Button Foo () { return (Button)FooImpl(); }
    // ...
}

class SuperButton : Button
{
    protected override Control<Object> FooImpl () { return this; }
    public new SuperButton Foo () { return (SuperButton)FooImpl(); }
    // ...
}

Кода больше, но иерархия может быть любой глубины.

Также есть вариант с интерфейсами, но он ещё многословнее.