2

一般的な抽象クラスとそのサブクラスに問題があり、解決方法がわかりません。次の抽象クラスがあります。

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

これは私の CommonPoco です:

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; }
    }
}

今、私はこのような Loader の他の多くのサブクラスも持っています:

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
    }

また、Country クラスを含む CommonPoco のサブクラスも多数あります。ここまでは順調ですね。さて、問題は、一般的なメソッドを実装したいと思います。このメソッドは、いくつかのパラメーターに基づいて、タスクに適切なローダーを使用します。多分このようなもの:

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

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

言い忘れましたが、.NET Framework 4.0 を使用しています。必要に応じて、抽象クラスであっても、必要なものは何でも変更します。ありがとう。抽象クラスの代わりにインターフェイスを使用すると、これが可能になるのでしょうか。

4

5 に答える 5

2

次のように、抽象クラスをインターフェイスに変換するとどうなりますか。

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 を複数回実装する必要があることです。

于 2012-05-11T18:21:47.647 に答える
0

Country文句を言う理由は次のとおりです。国に合格していない他のPOCOでそれをやろうとした場合はどうなりますか? 例: LoadModule<Continent>(Module.Country)? さて、何が起こるはずですか?あなたを困らせるのは、このあいまいさです。

ジェネリック型の要点は、渡す任意の型に対して機能することですが、ジェネリック定義に関係なく、ジェネリック変数に特定の型を設定しようとしているため、これは悪い習慣です (制約されている場合でも)。ジェネリックはジェネリックのままにします。つまり、一般的な定義を削除するだけで問題ありません。

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

これは、サブクラスができる/すべきことの「定義」としてインターフェイス/基本クラスを使用しているという点で、ポリモーフィズムの背後にある前提です。このように、変数を親/基本型 (この場合はLoaderCommonPoco) として指定すると、特定の実装 (つまり: CountryLoader) は、機能 (メソッドとプロパティ) のために親/基本型として扱われます。

于 2012-05-11T16:29:43.243 に答える
0

共変型と反変型の問題が発生しています。使用している C# のバージョンによっては、ジェネリック型を 'in' (共変) または 'out' (反変) マーカーでマークできる場合があります。たとえば、ローダーですが、この場合、クラスは共変 (安全になる) と反変 (安全に設定する) の両方の方法で機能しています。クラス定義を分割して両方のユース ケースをサポートするか、より複雑なスキームを使用して型解決を行うことができます。継承された型のオープン ジェネリック解決をサポートする IoC コンテナーを使用している場合 (Castle Windsor はサポートしますが、StructureMap はサポートしません)、特定のケースでどのローダーを使用するかをコンテナーに依存できます。

于 2012-05-11T16:31:31.387 に答える
0

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

public interface ILoader<out T>

次に、Loader 基本クラスにこれを実装させ、SPFiredrake で説明したのと同様のメソッドを使用します。ただし、ILoader<CommonPoco>.

于 2012-05-11T18:18:15.960 に答える
0

しかし、その後、誤ってこのようなものを呼び出す可能性があります

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

あるローダーを期待していたときに別のローダーを取得するのはコンパイラの障害です。@killinaswine の回答を参照してください。MS 開発者が、これを行うのは良い考えではないと考える理由です。

この問題は、型キャストを使用して自己責任で回避できます。

于 2012-05-11T18:33:35.997 に答える