С# Общие и полиморфизм: оксюморон?
Я просто хочу подтвердить, что я понял о Generics в С#. Это появилось в нескольких базовых кодах, с которыми я работал, когда общий базовый класс используется для создания производных экземпляров типа безопасных. Очень простой пример того, что я говорю,
public class SomeClass<T>
{
public virtual void SomeMethod(){ }
}
public class DeriveFrom :SomeClass<string>
{
public override void SomeMethod()
{
base.SomeMethod();
}
}
Проблема возникает, когда я хочу использовать производные экземпляры полиморфным способом.
public class ClientCode
{
public void DoSomethingClienty()
{
Factory factory = new Factory();
//Doesn't compile because SomeClass needs a type parameter!
SomeClass someInstance = factory.Create();
someInstance.SomeMethod();
}
}
Кажется, что когда вы вводите Generic в иерархию наследования или интерфейс, вы больше не сможете использовать это семейство классов полиморфным способом, за исключением, возможно, внутреннего для себя. Это правда?
Ответы
Ответ 1
Насколько я могу судить, для употребления кода не нужны особенности общего класса (т.е. он не зависит от того, что T
). Итак, почему бы вам не ввести интерфейс, который будет реализован SomeClass<T>
, и использовать экземпляр этого интерфейса.
например:.
public interface ISome
{
void SomeMethod();
}
public class SomeClass<T>: ISome
{
public virtual void SomeMethod(){ }
}
public void DoSomethingClienty()
{
Factory factory = new Factory();
ISome someInstance = factory.Create();
someInstance.SomeMethod();
}
Теперь подклассы SomeClass<T>
могут работать по-разному на разных T
s, но код потребления не изменится.
Ответ 2
Я думаю, вы неправильно понимаете суть дженериков. Generics позволяет вам обобщать класс, требующий типа, но не особенно заботится о том, какой тип. Например, List<string>
- это список строк, но что бы было List
? Это довольно бесполезная концепция, чтобы иметь список ничего.
Каждый специализированный класс (т.е. List<string>
) имеет свой собственный тип, и компилятор рассматривает его как таковой. Можно получить сам общий тип (typeof(List<>)
), но в большинстве случаев он бесполезен, и вы, конечно же, не можете сделать его экземпляр.
Ответ 3
Я бы предпочел использовать абстрактный класс, чтобы действовать как основа всех родовых типов.
public abstract class SomeClass
{
public abstract void SomeMethod();
}
public class SomeClass<T> : SomeClass
{
public override void SomeMethod() { }
}
public class DeriveFrom<String> : SomeClass<String>
{
public override void SomeMethod() { base.SomeMethod(); }
}
Ответ 4
Я думаю, что вы ищете:
SomeClass(of someType) someInstance = factory(of someType).Create();
or maybe
SomeClass(of someType) someInstance = factory.Create(of someType)();
or
SomeClass(of someType) someInstance = factory.Create();
Можно создать набор классов factory для создания разных общих классов или иметь factory с параметром generic type, чтобы указать, какой родовой тип он должен создать (обратите внимание, что в любом случае параметр типа является параметром типа для универсального класса, а не является самим общим классом). Также возможно иметь factory, который предназначен для возврата экземпляров одной конкретной формы родового типа.
Ответ 5
Кажется, что как только вы вводите Generic в иерархию или интерфейс наследования, вы больше не сможете использовать это семейство классов полиморфным способом.
Правильно, это очень похоже на эту ситуацию:
class StringContainer
{
}
class IntContainer
{
}
StringContainer container = new IntContainer(); // fails to compile
но вы можете сделать это:
class Container
{
}
class Container<T> : Container
{
}
Container container = new Container<String>(); // OK
Ответ 6
public Class ReflectionReport<T>
{
// This class uses Reflection to produce column-based grids for reporting.
// However, you need a type in order to know what the columns are.
}
... so, in another class you have...
public Class ReflectionReportProject {
ReflectionReport<Thang> thangReport = new ReflectionReport<Thang>();
ReflectionReport<Thong> thongReport = new ReflectionReport<Thong>();
... some more stuff ...
// Now, you want to pass off all of the reports to be processed at one time....
public ReflectionReport<????>[] ProvideReports()
{
return new ReflectionReport<????>[] { thangReport, thongReport } ;
}
}