web-dev-qa-db-ja.com

C#でのジェネリックスとの共分散逆分散の理解に関する問題

次のC#コードがなぜコンパイルされないのか理解できません。

ご覧のとおり、IEnumerable<T>パラメーターを持つ(そしてTIAインターフェースに制限されている)静的な汎用メソッドSomethingがあります。このパラメーターをIEnumerable<IA>に暗黙的に変換することはできません。

説明は何ですか? (私は回避策を探しません、なぜそれがうまくいかないのか理解するために)。

public interface IA { }
public interface IB : IA { }
public class CIA : IA { }
public class CIAD : CIA { }
public class CIB : IB { }
public class CIBD : CIB { }

public static class Test
{
    public static IList<T> Something<T>(IEnumerable<T> foo) where T : IA
    {
        var bar = foo.ToList();

        // All those calls are legal
        Something2(new List<IA>());
        Something2(new List<IB>());
        Something2(new List<CIA>());
        Something2(new List<CIAD>());
        Something2(new List<CIB>());
        Something2(new List<CIBD>());
        Something2(bar.Cast<IA>());

        // This call is illegal
        Something2(bar);

        return bar;
    }

    private static void Something2(IEnumerable<IA> foo)
    {
    }
}

Something2(bar)行に表示されるエラー

引数1: 'System.Collections.Generic.List'から 'System.Collections.Generic.IEnumerable'に変換できません

115
BenLaz

エラーメッセージは十分に有益ではありません、そしてそれは私のせいです。それについてすみません。

あなたが経験している問題は、共分散が参照型にのみ作用するという事実の結果です。

あなたはたぶん「今IAは参照型です」と言っているでしょう。はい、そうです。しかし、TIAと等しいとは言わなかった。 TIAを実装する型であり、 値型はインターフェースを実装することができます 。したがって、共分散が機能するかどうかはわかりません。また、それを許可しません。

共分散を機能させたい場合は、typeパラメーターがclass制約およびIAインターフェース制約を持つ参照型であることをコンパイラーに伝えなければなりません。

共分散には参照型の保証が必要であるため、エラーメッセージには変換が不可能であることが実際には伝えられます。これが基本的な問題であるためです。

219
Eric Lippert

私はEricの優れたインサイダー回答を、一般的な制約に慣れていないかもしれないコード例で補完したいだけでした。

Somethingのシグネチャを次のように変更します。class制約 が最初にくる必要があります

public static IList<T> Something<T>(IEnumerable<T> foo) where T : class, IA
26
Marcell Toth