13

次のコードスニペットが共変ではない理由がわかりません。

  public interface IResourceColl<out T> : IEnumerable<T> where T : IResource {

    int Count { get; }

    T this[int index] { get; }

    bool TryGetValue( string SUID, out T obj ); // Error here?
    }

エラー1無効な分散:タイプパラメータ「T」は、「IResourceColl.TryGetValue(string、outT)」で常に有効である必要があります。「T」は共変です。

私のインターフェースは、出力位置でテンプレートパラメーターのみを使用します。このコードを次のようなものに簡単にリファクタリングできます

  public interface IResourceColl<out T> : IEnumerable<T> where T : class, IResource {

    int Count { get; }

    T this[int index] { get; }

    T TryGetValue( string SUID ); // return null if not found
    }

しかし、元のコードが実際に共分散に違反しているかどうか、またはこれがコンパイラまたは.NETの共分散の制限であるかどうかを理解しようとしています。

4

4 に答える 4

14

問題は確かにここにあります:

bool TryGetValue( string SUID, out T obj ); // Error here?

objをoutパラメーターとしてマークしました。これは、渡すだけでなく、型のインスタンスを渡すだけでなく、それを返すため、共変ではないことを意味します。 objT

編集:

Eric Lippertは、「C#でパラメーターを参照および出力し、バリアントとしてマークすることはできません」に対する彼の回答を参照し、パラメーターに関して彼を引用する人よりも優れていると述べていoutます。

Tを「アウト」としてマークすることは合法である必要がありますか?残念だけど違う。「out」は、実際には舞台裏の「ref」と同じです。「out」と「ref」の唯一の違いは、コンパイラーが、呼び出し先によって割り当てられる前にoutパラメーターからの読み取りを禁止していることと、呼び出し先が正常に戻る前にコンパイラーが割り当てを要求することです。このインターフェイスの実装をC#以外の.NET言語で記述した人は、初期化される前にアイテムから読み取ることができるため、入力として使用できます。したがって、この場合、Tを「アウト」としてマークすることは禁止されています。それは残念ですが、私たちにできることは何もありません。CLRの型安全性規則に従わなければなりません。

于 2012-01-18T16:45:53.210 に答える
4

拡張メソッドを使用した場合の可能な回避策は次のとおりです。実装者の観点からは必ずしも便利ではありませんが、ユーザーは満足している必要があります。

public interface IExample<out T>
{
    T TryGetByName(string name, out bool success);
}

public static class HelperClass
{
    public static bool TryGetByName<T>(this IExample<T> @this, string name, out T child)
    {
        bool success;
        child = @this.TryGetByName(name, out success);
        return success;
    }
}

public interface IAnimal { };

public interface IFish : IAnimal { };

public class XavierTheFish : IFish { };

public class Aquarium : IExample<IFish>
{
    public IFish TryGetByName(string name, out bool success)
    {
        if (name == "Xavier")
        {
            success = true;
            return new XavierTheFish();
        }
        else
        {
            success = false;
            return null;
        }
    }
}

public static class Test
{
    public static void Main()
    {
        var aquarium = new Aquarium();
        IAnimal child;
        if (aquarium.TryGetByName("Xavier", out child))
        {
            Console.WriteLine(child);
        }
    }
}
于 2014-10-10T12:54:22.133 に答える
1

出力パラメーターに提供される値は、出力パラメーター宣言とまったく同じタイプでなければならないため、共分散に違反します。たとえばT、文字列であると仮定すると、共分散は、実行しても問題がないことを意味します。

var someIResourceColl = new someIResourceCollClass<String>();
Object k;
someIResourceColl.TryGetValue("Foo", out k); // This will break because k is an Object, not a String
于 2012-01-18T16:48:25.070 に答える
1

この小さな例を調べると、それが許可されていない理由がわかります。

public void Test()
{
    string s = "Hello";
    Foo(out s);
}

public void Foo(out string s) //s is passed with "Hello" even if not usable
{
    s = "Bye";
}

outつまり、実行がメソッドを離れる前に確実に割り当てられる必要があり、逆に、メソッド本体で確実に割り当てられるまでs使用できません。これは、共分散ルールsと互換性があるようです。ただし、メソッドを呼び出す前に、呼び出しサイトで割り当てることを妨げるものは何もありません。この値はメソッドに渡されます。つまり、使用できない場合でも、ジェネリック型は戻り値としてのみ使用できるという共分散の規則に反するメソッドに、定義された型のパラメーターを効果的に渡すことができます。メソッドのタイプ。s

于 2012-01-18T17:15:11.900 に答える