Дженерики C# и полиморфизм:оксюморон?
-
06-07-2019 - |
Вопрос
Я просто хочу подтвердить то, что я понял о дженериках в C#.Это возникало в нескольких базах кода, над которыми я работал, где универсальный базовый класс использовался для создания типобезопасных производных экземпляров.Очень простой пример того, о чем я говорю,
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 в иерархию наследования или интерфейс, вы больше не сможете использовать это семейство классов полиморфным образом, за исключением, возможно, внутреннего по отношению к себе.Это правда?
Решение
Насколько я понимаю, для использования кода не нужны особенности универсального класса (т. е. это не зависит от того, какой 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, но потребляющий код не изменится.
Другие советы
Я думаю, вы неправильно понимаете суть дженериков. Обобщения позволяют вам обобщать класс, который требует тип, но не особенно заботится о том, что это за тип. Например, List<string>
- это список строк, но что будет List
? Это довольно бесполезная концепция, чтобы иметь список из ничего.
Каждый специализированный класс (т. е. typeof(List<>)
) имеет свой собственный отдельный тип , и компилятор рассматривает его как таковой. возможно получить сам универсальный тип (например, <=>), но в большинстве случаев он бесполезен, и вы определенно не можете создать его экземпляр.
Я бы предпочел использовать абстрактный класс в качестве основы для всех универсальных типов.
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(); }
}
Кажется, что после того, как вы ввели Generic в иерархию или интерфейс наследования, вы больше не можете использовать это семейство классов полиморфным способом
Правильно, это очень похоже на эту ситуацию:
class StringContainer
{
}
class IntContainer
{
}
StringContainer container = new IntContainer(); // fails to compile
но вы могли бы сделать это:
class Container
{
}
class Container<T> : Container
{
}
Container container = new Container<String>(); // OK
Я думаю, что вы ищете:
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();
Можно иметь набор фабричных классов для создания различных универсальных классов или фабрику с параметром универсального типа, чтобы указать, какой универсальный тип он должен создать (обратите внимание, что в любом случае параметр типа является параметром типа для универсального класса, а не сам универсальный класс). Также возможно иметь фабрику, которая предназначена для возврата экземпляров одной конкретной формы универсального типа.
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 } ;
}
}