C#ジェネリックとポリモーフィズム:矛盾表現?
-
06-07-2019 - |
質問
C#のジェネリックについて理解したことを確認したいだけです。これは、タイプセーフな派生インスタンスを作成するために汎用の基本クラスを使用する作業を行った2つのコードベースで発生しました。私が話していることの非常に簡単な例、
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();
}
}
ジェネリックを継承階層またはインターフェイスに導入すると、おそらくそれ自体の内部を除いて、そのクラスファミリを多態的に使用することはできなくなるようです。本当ですか?
解決
私が見る限り、コードを消費するためにジェネリッククラスの詳細は必要ありません(つまり、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();
}
現在、<=>のサブクラスは異なる<=>で異なる動作をすることができますが、消費するコードは変わりません。
他のヒント
ジェネリックのポイントを誤解していると思います。 Genericsを使用すると、型を必要とするクラスを一般化できますが、どの型であるかは特に気にしません。たとえば、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(); }
}
ジェネリックを継承階層またはインターフェイスに導入すると、そのクラスのファミリをポリモーフィックな方法で使用できなくなるようです
正解、これはこの状況によく似ています:
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 } ;
}
}