9

Java では、ジェネリクスとワイルドカードの操作にかなり慣れています。次のようなもの: List<? extends Animal>. これにより、動物のサブタイプのコレクションを取得し、各要素に対して一般的なルーチンを実行できます (例: makeNoise())。私は C# でこれを達成しようとしていますが、ワイルドカードがないため、少し混乱しています。

ドメインに関しては、ここで行っていることは、SQL SMO ライブラリを使用して、データベースからスクリプトを収集することです。さまざまなオブジェクト (テーブル、ビュー、関数など - これは T です) をスクリプト化して収集するために、何度も拡張された基本インターフェイス タイプがあります。

public interface IScripter<T> where T : IScriptable
{
    IList<T> CollectScripts(params...)
}

public abstract class AbstractScripter<T> : IScripter<T> where T : IScriptable
{
    ....
}

public class TableScripter : AbstractScripter<Table>
{
    ....
}

public class ViewScripter : AbstractScripter<View>
{
    ....
}

ここまでは順調ですね。完全に理にかなったオブジェクト階層のようですね。ワイルドカードがないことがわかるまで、私が意図したことは次のとおりです。

public class Program
{
    static void Main(string[] args)
    {
        // auto discover all scripter modules, table, view, etc
        IList<Iscripter<? extends IScriptable>> allScripters = GetAllScripterModules(); 
        foreach (IScripter<? extends IScriptable> scripter in allScripters)
        {
            IList<? extends IScriptable> scriptedObjects = scripter.CollectScripts(...);
            // do something with scripted objects
        }
    }
 }

ここ<? extends IScriptable>には存在しないので、代わりに何をすべきですか?私は多くのこと、一般的な方法、基本型を使用するだけ、あらゆる種類の厄介なキャストを試しましたが、実際には何もうまくいきませんでした。

IList<Iscripter<? extends IScriptable>部品を交換するために何を提案しますか?

ティア

4

2 に答える 2

12

使用して、またはインターフェイスから他の共変インターフェイスoutのみを渡すことにより、インターフェイスを共変にすることができます。TT

public interface IScripter<out T> where T : IScriptable
{
    IEnumerable<T> CollectScripts(params...)
}

非共変を使用できないため、結果に追加することはできません。追加する場合は、IList別のインターフェイスを追加します。

public interface IScripterAddable<T> where T : IScriptable
{
    //either:
    IList<T> CollectScripts(params...)
    //or add just what you need to, this is usually better
    //than exposing the underlying IList - basic encapsulation principles
    void AddScript(...)
}

次に、を削除し? extendsます。

    // auto discover all scripter modules, table, view, etc
    IList<Iscripter<IScriptable>> allScripters = GetAllScripterModules(); 
    foreach (IScripter<IScriptable> scripter in allScripters)
    {
        IEnumerable<IScriptable> scriptedObjects = scripter.CollectScripts(...);
        // do something with scripted objects
    }
于 2013-03-27T15:42:27.443 に答える
12

共分散とは、「リンゴが果物なら、リンゴのボウルは果物のボウルである」という性質です。これはすぐに問題を引き起こします: 果物の入ったボウルにオレンジを入れることができます. リンゴのボウルが果物のボウルで、オレンジを果物のボウルに入れることができる場合、リンゴのボウルにオレンジを入れることができます. その時点で、それはもはやリンゴのボウルではありません。

C# と Java は、このタイプ セーフ違反を防ぐために 2 つの異なるアプローチを採用しています。C# のアプローチは、共変インターフェイスはその共分散を前もって宣言する必要があり、インターフェイスは型の安全性を侵害するために使用される可能性のあるメソッドを一切公開しないと言うことです。

したがってIEnumerable<T>、C# の T では共変です。これは、オレンジを一連のリンゴに入れる方法がないためです。には「追加」メソッドはありませんIEnumerable<T>。Add メソッドがあるIList<T>ため、C# では共変ではありません。

Java は別のアプローチをとります。「実際にオレンジを追加しない限り、このリンゴのボウルをフルーツのボウルとして今すぐ使用できます。差異は、インターフェイスの全体的なプロパティではなく、特定の場所で発生します。

実際の質問に対処するには: IScripter<T>T でインターフェイスを共変にすることができない場合はIList<T>、 を返すことができるため、スタックする可能性があります。しかし、それを含めることができればIEnumerable<T>、幸運かもしれません。インターフェイスを としてマークし、それが「出力」位置でのみ使用されるIScripter<out T>ことを確認します。T

于 2013-03-27T16:40:47.153 に答える