2012-05-11 20 views
1

ジェネリックな抽象クラスに問題があり、それはサブクラスなので、どうやって解決するのか分かりません。一般的な抽象クラスとジェネリックメソッド

public class CommonPoco { 
    private int line; 
    public int Line { 
     get { return line; } 
     set { line = value; } 
    } 

    private string error; 
    public string Error { 
     get { return error; } 
     set { error = value; } 
    } 
} 

は今、私もこの1のようにローダーの他の多くのサブクラスを持っている:

public class CountryLoader: Loader<Country> { 
    public override List<Country> LoadFromFile(StreamReader input, 
     out List<Country> result, bool trackProgress, IFormatProvider format) { 
     //Method implementation 
    } 
    public override Country UploadToDb(List<Country> data) { 
     //Method implementation 
    } 

そして、私も持っている。これは私のCommonPocoある

public abstract class Loader<T> where T: CommonPoco { 
    public virtual List<T> LoadFromFile(StreamReader input, out List<T> result, bool trackProgress) { 
     return LoadFromFile(input, out result, trackProgress, CultureInfo.CurrentCulture); 
    } 
    public abstract List<T> LoadFromFile(StreamReader input, out List<T> result, bool trackProgress, IFormatProvider format); 
    public abstract T UploadToDb(List<T> data); 
} 

:私は、次の抽象クラスを持っていますCountryクラスを含むCommonPocoの多くのサブクラス。ここまでは順調ですね。今問題に、私は、一般的な?、メソッドを実装したいと思います。このメソッドは、いくつかのパラメーターに基づいて、タスクに適切なローダーを使用します。たぶん、このような何か:

void LoadModule<T>(Module module) where T: CommonPoco { 
    Loader<T> loader; 
    switch (module) { 
     case Module.Country: 
      loader = new CountryLoader(); 
      break; 
    } 
} 

これは動作しないと、コンパイラは、それがCountryLoaderからローダーに変換することはできませんと言って文句を言います。私はそれぞれのモジュールをロードするメソッドを作成しなければならず、Loaderクラスの初期化を除いて全く同じコードです。私は本当に重複したコードを嫌い、どのように私はこれを達成することができますか?

.NET Framework 4.0を使用しています。 私は必要なら何でも、私の抽象クラスでさえも変えたいと思っています。ありがとう。 抽象クラスの代わりにインターフェイスを使用すると、これを行うことができるのだろうかと思います。

+0

キャッスルウィンザーなどのIoCコンテナを使用することも、リフレクションだけを使用することもできます。しかし、 'List 'パラメータを 'List '戻り値を持つメソッドに持つ理由は何ですか?それは非常に奇妙なようです。 – phoog

+2

'Loader '(これは 'CountryLoader'です)は' Loader 'ではありません。 したがって、 'Country'は' CommonPoco'から継承されるかもしれませんが(上記のOPには関係はありませんが)、汎用クラスは継承ではありません。 – Tejs

+0

また、これは通常、人々が非タイプの特定のデータ(この場合は 'module')を渡し、強く型付けされた結果を期待するときに起こります。結果のタイプを判別できない場合は、そのデータへのアクセスをどのように計画するか再考する必要があります。 – Tejs

答えて

0

あなたはこのように、インタフェースにあなたの抽象クラスをオンにしたらどう:

public interface ILoader<T> where T : CommonPoco 
    { 
     List<T> LoadFromFile(StreamReader input, out List<T> result, bool trackProgress); 
     List<T> LoadFromFile(StreamReader input, out List<T> result, bool trackProgress, IFormatProvider format); 
     T UploadToDb(List<T> data); 
    } 

その後、あなたのCountryLoaderの実装を変更:

public class CountryLoader : ILoader<Country> 
    { 
     public List<Country> LoadFromFile(StreamReader input, out List<Country> result, bool trackProgress) 
     { 
      return LoadFromFile(input, out result, trackProgress, CultureInfo.CurrentCulture); 
     } 

     public List<Country> LoadFromFile(StreamReader input, 
      out List<Country> result, bool trackProgress, IFormatProvider format) 
     { 
      //Method implementation 
      result = null; 
      return null; 
     } 

     public Country UploadToDb(List<Country> data) 
     { 
      //Method implementation 
      return null; 
     } 
    } 

そして、あなたは、このような方法で作成します:

void LoadModule<T>(Module module) where T:CommonPoco 
     { 
      ILoader<T> loader; 
      switch (module) 
      { 
       case Module.Country: 
        loader = new CountryLoader() as ILoader<T>; 
        break; 
      } 
     } 

この問題は、一般的なLoadFromFileを実装する必要があることです他の誰かがそれを回避する方法を持っていない限り、何度も繰り返します。

+1

ありがとう、これは動作します。明確にするために、インターフェイスに変換する必要はありません。抽象クラスはうまく動作します。キーラインは、 'loader = new CountryLoader()as Loader ;'です。 – user1387786

0

ここにそれが文句を言う理由があります:もしあなたがCountryではないが国を通過する他のPOCOでそれをしようとすれば?例:LoadModule<Continent>(Module.Country)?今何が起こるはずですか?あなたを困らせるのはこのあいまいさです。

ジェネリック型の全体的なポイントは、渡すANY型では機能しますが、一般的な定義にかかわらず、ジェネリック変数に特定の型を設定しようとしていることです。拘束されたとき)。一般ジェネリックを保つ。つまり、一般的な定義を削除するだけで、うまくいくはずです。

void LoadModule(Module module) { 
    Loader<CommonPoco> loader; 
    switch (module) { 
     case Module.Country: 
      loader = (Loader<CommonPoco>)new CountryLoader(); 
      break; 
    } 
} 

これはその中で、あなたが/やるべきことができますどのようなサブクラスの「定義」としてインタフェース/ベース・クラスを使用している、多型の背後にある前提です。このように、変数を親/基底型(この場合はLoaderCommonPoco)として指定し、特定の実装(つまり:CountryLoader)を機能(メソッドとプロパティ)の目的で親/基底型として扱います。

+0

返信いただきありがとうございます、これを試しても動作しません。それでも同じ問題が発生します。 – user1387786

+0

SPFiredrakeが提供したコードがcontravariantly型を使用しようとしているため、動作しません。 Co/Contravarianceはインターフェイスでのみ機能します。 – MgSam

+0

編集済みですが、これはまだ動作しません。長い話をすると、 'Country'オブジェクトは' CommonPoco'オブジェクトと共変ですが、 'Loader 'オブジェクトは 'Loader 'と共変できません。私の例では、インターフェースで試してみましたが、(Loaderはインターフェースではなく抽象クラスであるので)それを好きではありませんでした。 http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx – SPFiredrake

0

あなたは共変型および反変種の問題に遭遇しています。使用しているC#のバージョンによっては、 'in'(共変)または 'out'(反変)マーカーでジェネリックタイプをマークすることができます。たとえば、Loaderですが、この場合、クラスは共変(安全)と反禁制(安全)の両方の方法で機能しています。両方のユースケースをサポートするようにクラス定義を分割することも、タイプ解決のためのより精巧なスキームを使用することもできます。継承された型のオープンジェネリックな解決をサポートするIoCコンテナを使用している場合(Castle Windsorは、StructureMapはありません)、コンテナを使用して特定のケースで使用するLoaderを特定できます。

+0

申し訳ありませんが、このプロジェクトで.NET Framework 4.0を使用しています。もう一度申し訳ありませんが、キャッスルウィンザーやストラクチャーマップが何であるか分かりません。 – user1387786

+1

Co/contravariance(in/out)は、クラスではなくインターフェイスでのみ使用できます。 – MgSam

+0

Castle WindsorとStructureMapはInversion of Controlコンテナです。オブジェクト作成責任をカプセル化します(Factory、Builder、Service Locatorのパターンと考える)。彼らは型定義を調べて、a)依存関係を含めてそれをどのように構築するか、b)どのような規則が構成されているかに基づいて特定の型の要求を満たす方法を決定することができます。 – pfries

0

ローダー<のメソッドを反例としてマークされたインターフェイスに抽象化することができます。

public interface ILoader<out T> 

そしてローダ基底クラスがこれを実装有し、その後SPFiredrakeがILoader<CommonPoco>を除いて、記載したものと同様の方法を使用して。

0

が、その後、あなたが誤って呼び出すことができます。この

villageLoader = LoadModule<Village>(**countryModuleType**) ; 

のようなもの、あなたが1つのローダーを期待するときに別のものを得ることを、コンパイラの障害になります。 @killinaswineを参照してくださいなぜms開発者はあなたにこれをさせるのは良い考えではないと思っていますか?

この問題を回避するには、型キャストを実行する際に自己責任で行うことができます。

関連する問題