2009-11-29 16 views
16

C#のGenericsについて私が理解していることを確認したいだけです。これは、タイプセーフな派生インスタンスを作成するためにジェネリックベースクラスが使用されているところで私が働いていた2つのコードベースにあります。私が話しているかの非常に単純な例、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(); 
    } 
} 

あなたが継承階層またはインタフェースにジェネリックを導入したら、あなたはもはや自分自身におそらく内部を除いて多型の方法で、クラスのファミリを使用することはできないようです。本当?

+3

SomeClass はSomeClassではなく、DeriveFromの基本クラスであると思われます。これはあなたがそう信じていると思われるものです。 – Thanatos

答えて

11

私が見ている限り、消費コードはジェネリッククラスの詳細を必要としません(つまり、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の上で異なる動作をすることができますが、消費するコードは変更されません。

+0

私はこれが最善の解決策だと思います。クライアントが気にするインターフェースだけを拡張して、フードの下にジェネリックスを隠します。 –

9

ジェネリックの点を誤解していると思います。ジェネリックスを使用すると、タイプを必要とするクラスを一般化することができますが、そのタイプは特に気にしません。たとえば、List<string>は文字列のリストですが、Listは何ですか?何もないリストを持つことはむしろ無駄な概念です。

それぞれの特殊クラス(List<string>)はです。それは独自のタイプです。コンパイラはそれをそのように扱います。汎用タイプ(例えばtypeof(List<>))を取得するには、可能なですが、ほとんどの場合、それは役に立たず、確かにそのインスタンスを作成することはできません。

2

抽象クラスを使用して、すべてのジェネリック型のベースとして機能することをお勧めします。私はあなたが探しているものだと思い

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(); }    
} 
+0

実際、「ファクトリー」はここでどのように機能しますか? –

+0

Nickが言及した "Factory"の実装については考えていません。しかし、私は非常によく、抽象的で非ジェネリックな 'SomeClass'で実装できると確信しています。 –

+0

@Ambyしかし、抽象的なvoid SomeMethod()の戻り値の型がTで、パラメータの型がTの場合はどうしますか?その後、非ジェネリック抽象クラスの下でジェネリックスを非表示にすることはできません。私はあなたからの答えやヒントを感謝するでしょう:) – Elisabeth

0

は次のとおりです。

 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(); 

それは別の一般的なクラスを作成するファクトリクラスのセットを持つことが可能だ、またはそれにジェネリック型パラメータを持つ工場を持っていますどちらのジェネリック型を作成するかを指定します(いずれの場合も、typeパラメータはジェネリッククラス自体ではなくジェネリッククラスの型パラメータです)。特定のジェネリック型のインスタンスを返すように設計されたファクトリを持つこともできます。

0

あなたが継承階層またはインタフェースにジェネリックを導入したら、あなたはもはや正しい

多型の方法で、クラスのファミリを使用することはできません、それはこのような状況に非常に似ているようだ。

class StringContainer 
{ 
} 

class IntContainer 
{ 
} 

StringContainer container = new IntContainer(); // fails to compile 

が、あなたはこれを行うことができます:

class Container 
{ 
} 

class Container<T> : Container 
{ 
} 

Container container = new Container<String>(); // OK 
0
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 } ; 
    } 
} 
+2

上記は、ジェネリックの多形性が本当に有用である方法の例です。 – user1126454