6

次のコードでは、クラスを期待しているコンストラクターに構造体を渡します。なぜこれはエラーなしでコンパイルおよび実行される(そして目的の出力を生成する)のですか?

class Program
{
    static void Main()
    {
        var entity = new Foo { Id = 3 };
        var t = new Test<IEntity>(entity); // why doesn't this fail?
        Console.WriteLine(t.Entity.Id.ToString());
        Console.ReadKey();
    }
}

public class Test<TEntity> where TEntity : class
{
    public TEntity Entity { get; set; }

    public Test(TEntity entity)
    {
        Entity = entity;
    }

    public void ClearEntity()
    {
        Entity = null;
    }
}

public struct Foo : IEntity
{
    public int Id { get; set; }
}

public interface IEntity
{
    int Id { get; set; }
}

以下に示すように、Main()への呼び出しが含まれるようにメソッドを変更して、エラーは生成されません。なんで?ClearEntity()

static void Main()
{
    var entity = new Foo { Id = 3 };
    var t = new Test<IEntity>(entity);
    Console.WriteLine(t.Entity.Id.ToString());
    t.ClearEntity(); // why doesn't this fail?
    Console.ReadKey();
}
4

4 に答える 4

8

where TEntity : class強制TEntity的に参照型になりますが、 などのインターフェイスIEntityは参照型です。

ここを参照してください: http://msdn.microsoft.com/en-us/library/d5x73970(v=vs.80).aspx

T : クラス | 型引数は、任意のクラス、インターフェイス、デリゲート、または配列型を含む参照型である必要があります

t.ClearEntity()2 番目の質問については、型が値型である変数に null を割り当てているため、失敗すると思うかもしれませんが、そうではありません。のコンパイル時の型Entityは参照型IEntityであり、実行時の型 (代入後) は null 型です。したがって、 type の変数ではFooなく valueを持つことはありませんnull

于 2012-09-21T20:02:48.160 に答える
2

C# のドキュメントから:

T : クラス

型引数は、任意のクラス、インターフェイス、デリゲート、または配列型を含む参照型である必要があります。(以下の注を参照してください。)

インターフェイスを介して構造体を渡しているため、参照型と見なされます。

于 2012-09-21T20:03:09.630 に答える
1

.net ランタイム内では、すべての null 非許容値型には、System.ValueType. 言ってObject Foo = 5;も実際にはに格納Int32されませんFoo; 代わりに、関連付けられた参照型の新しいインスタンスを作成し、Int32そのインスタンスへの参照を格納します。ジェネリック型のclass制約は、問題の型が何らかの参照型でなければならないことを指定しますが、それ自体は、型がボックス化された値型インスタンスへの参照を渡すために使用される可能性を排除しません。ジェネリック型の制約の外側のほとんどのコンテキストでは、インターフェイス型はクラス型と見なされます。

ボックス化された値型が参照型のように格納されるだけではないことに注意することが重要です。それらは参照型のように振る舞います。たとえば、List<string>.Enumeratorは を実装する値型IEnumerator<string>です。一方が type の 2 つの変数を持っている場合List<string>.Enumerator、一方を他方にコピーすると列挙の状態がコピーされ、同じリストを指す 2 つの別個の独立した列挙子が存在するようになります。これらの変数の 1 つを型の変数にコピーするIEnumerator<string>と、関連付けられたボックス化された値型の新しいインスタンスが作成List<string.Enumeratorされ、後者の変数にその新しいオブジェクトへの参照が格納されます (これは 3 番目の独立した列挙子になります)。ただし、その変数を type の別の変数にコピーするIEnumerator<string>と、既存のオブジェクトへの参照が格納されるだけです (IEnumerator<string>は参照型であるため)。

C# 言語は、値型が から派生したふりをしようとしますObjectが、.net ランタイムの内部では実際にはそうではありません。代わりに、から派生する型に変換System.ValueTypeできます(これは から派生しますObject)。後者の型は型制約を満たしますが、前者は型制約を満たしません。ちなみに、その名前にもかかわらず、System.ValueType実際にはクラス型です。

于 2012-09-21T20:59:38.383 に答える
0

同様に、制約キーワードclassは型宣言キーワードと同じクラスを意味すると想定してclassいましたが、そうではありません。

他の回答で説明されているように、classここでの用語は過負荷です。これは、C# 言語設計にとって恐ろしい決定であるように思えます。のようなものreferencetypeがもっと役に立ちました。

于 2013-10-19T23:18:04.980 に答える