Наследование по параметру с ограниченным общим типом
Я знаю, что невозможно наследовать от типового параметра типа, но это было бы удобно при реализации общего прокси для производных абстрактного типа: -)
Кто-нибудь знает, почему это невозможно?
Пример С#:
abstract class Foo
{
public virtual void Bar()
{
// nop
}
}
class FooProxy<TFoo> : TFoo
where TFoo : Foo
{
public override void Bar()
{
// do some stuff before
base.Bar();
// do some stuff after
}
}
EDIT: Еще один код, чтобы проиллюстрировать пример того, как это можно использовать. Рассмотрим следующие производные от Foo:
class FooX : Foo
{
public string X { get; set; }
public override void Bar()
{
Console.WriteLine("Doing Bar X");
}
}
class FooY : Foo
{
public string Y { get; set; }
public override void Bar()
{
Console.WriteLine("Doing Bar Y");
}
}
И вызывающий код:
FooProxy<FooX> fooXProxy = new FooProxy<FooX>();
fooXProxy.X = "test X";
fooXProxy.Bar();
FooProxy<FooY> fooYProxy = new FooProxy<FooY>();
fooYProxy.Y = "test Y";
fooYProxy.Bar();
Код в переопределении FooProxy метода Bar() будет использоваться повторно при использовании FooX и FooY.
РЕДАКТИРОВАТЬ: Пересмотрено согласно Pete OHanlon answer: сделанный метод Bar() виртуальный.
Ответы
Ответ 1
Потому что вы не можете. Дженерики - это не шаблоны. Вы не должны думать о них, как шаблоны С++, и ожидать такого же поведения. Это принципиально разные понятия.
Спецификация С# явно запрещает использование параметров типа в качестве базового класса:
Спецификация языка С# 3.0: параметры типа (§4.5)
Параметр типа нельзя использовать непосредственно для объявления базового класса (§10.2.4) или интерфейса (§13.1.3).
Обновление:
Я понимаю, что вы хотите сделать и его использование. Это традиционный пример использования шаблонов С++. В частности, если это было возможно сделать с использованием генераторов С#, такие вещи, как Moq
library, могли бы извлечь из этого выгоду. Проблема в том, что С++-шаблоны - это время компиляции "find and replace", в то время как С# generics - это время выполнения.
Чтобы продемонстрировать этот факт, для этого класса:
class Test<T> where T : class {
// whatever contents it might have...
}
во время компиляции и во время выполнения выделяется только один IL, компилятор JIT генерирует собственный одиночный собственный код для всех типов типа ссылочного типа. Это совсем не похоже на С++-шаблоны, где собственный код будет выделяться для каждого T
отдельно (он будет оптимизирован, но концептуально, они являются полностью отдельными фрагментами кода).
Ответ 2
Вы не можете наследовать от параметра универсального типа, потому что тип не известен во время компиляции, поэтому компилятор не может понять, что такое суперкласс. Я понимаю, что на первый взгляд тот факт, что компилятор может определить, что <T> похоже, предполагает, что он должен иметь возможность выяснить, что такое T, но две вещи разные.
Кроме того, у вас есть логическая проблема в Bar. Вы не можете вызвать base.Bar, потому что это абстрактный тип. Чтобы исправить это, вам нужно будет изменить вашу реализацию в Foo на
public virtual void Bar() {}
Ответ 3
Поскольку Foo является абстрактным типом.
Если вы внедрили Foo в класс, а затем использовали его в шаблоне, вы могли бы это сделать.
Вы также можете использовать интерфейс, чтобы делать то, что вы пытаетесь сделать, я верю.
Edit:
Во второй раз, глядя на ваш код, это бессмысленно. Вам нужно будет создать базовый класс типа GENERIC, а затем получить его, чтобы достичь того, что вы пытаетесь сделать. Это также придало бы еще больший смысл...
abstract class Foo<T>
{
public virtual void Bar();
}