web-dev-qa-db-ja.com

構造体、インターフェース、ボクシング

重複の可能性:
構造体がインターフェースを実装するのは安全ですか?

このコードを取る:

interface ISomeInterface
{
    public int SomeProperty { get; }
}

struct SomeStruct : ISomeInterface
{
    int someValue;

    public int SomeProperty { get { return someValue; } }

    public SomeStruct(int value)
    {
        someValue = value;
    }
}

そして、私はこれをどこかで行います:

ISomeInterface someVariable = new SomeStruct(2);

この場合、SomeStructはボックス化されていますか?

44
Sekhat

はい、そうです。基本的に、参照が必要で、値型の値しかない場合は、値がボックスで囲まれています。

ここで、ISomeInterfaceはインターフェイスであり、参照型です。したがって、someVariableの値は常に参照であるため、新しく作成された構造体の値はボックス化する必要があります。

56
Jon Skeet

ジョンの主張は真実ですが、補足として、規則にはわずかな例外が1つあります。ジェネリック。あなたが持っている場合 where T : ISomeInterfaceの場合、これはconstrainedであり、 special opcode を使用します。これは、インターフェイスを使用できることを意味しますwithoutボクシング。例えば:

public static void Foo<T>(T obj) where T : ISomeInterface {
    obj.Bar(); // Bar defined on ISomeInterface
}

これは、値型Tの場合でも、notボクシングを含みます。ただし、(同じFoo内で)次の場合:

ISomeInterface asInterface = obj;
asInterface.Bar();

その後、前と同じようにそのボックス。 constrainedonlyTに直接適用されます。

69
Marc Gravell

これを追加して、JonとMarcが提供する回答にもう少し光を当てることを願っています。

この非ジェネリックメソッドを検討してください。

public static void SetToNull(ref ISomeInterface obj) {
    obj = null;
}

うーん...refパラメータをnullに設定しています。それはおそらく参照型の場合だけですよね? (まあ、またはNullable<T>の場合ですが、単純にするためにその場合は無視しましょう。)したがって、このメソッドがコンパイルされるという事実は、何らかのインターフェイス型であると宣言された変数を参照型として扱う必要があることを示しています。

ここでのキーフレーズは「宣言済み」です。上記のメソッドを呼び出すこの試みを検討してください。

var x = new SomeStruct();

// This line does not compile:
// "Cannot convert from ref SomeStruct to ref ISomeInterface" --
// since x is declared to be of type SomeStruct, it cannot be passed
// to a method that wants a parameter of type ref ISomeInterface.
SetToNull(ref x);

確かに、上記のコードでxSetToNullに渡すことができない理由は、xISomeInterfaceとして宣言する必要があるためです。コンパイラはSetToNullに行ref xが含まれていることを魔法のように知っているため、obj = null-およびnotを渡すことができます。しかし、私の主張を補強する方法で:obj = null行は合法です正確にはそれはillegalは、ISomeInterfaceとして宣言された変数notをメソッドに渡します。

言い換えると、変数がISomeInterfaceとして宣言されている場合は、null、純粋、および単純に設定できます。これは、インターフェイスが参照型であるためです。したがって、オブジェクトをインターフェイスとして宣言し、それを値型オブジェクトに割り当てると、その値がボックス化されます。

一方、ここで、この架空の一般的な方法を考えてみましょう。

// This method does not compile:
// "Cannot convert null to type parameter 'T' because it could be 
// a non-nullable value type. Consider using 'default(T)' instead." --
// since this method could take a variable declared as, e.g., a SomeStruct,
// the compiler cannot assume a null assignment is legal.
public static void SetToNull<T>(ref T obj) where T : ISomeInterface {
    obj = null;
}
9
Dan Tao

MSDNドキュメント は、構造体が値であり、参照型ではないことを示しています。 object型の変数との間で変換する場合、これらはボックスで囲まれています。しかし、ここでの中心的な質問は、インターフェイスタイプの変数はどうでしょうか。インターフェイスはクラスによっても実装できるため、Jon Skeetがすでに述べたように、これは値から参照型への変換と同等である必要があります。したがって、はいボクシングが発生します。 msdnブログでさらに議論

0
sfuqua