6

リストから構造体の参照にアクセスして変更を加えることは可能ですか? スレッドバイレザ

したがって、次のことを考慮してstructくださいinterface(間違いなくあまり役​​に立ちませんが、問題を示すためだけに):

public interface IChangeStruct
{
    int Value { get; }
    void Change(int value);
}

public struct MyStruct : IChangeStruct
{
    int value;

    public MyStruct(int _value)
    {
        value = _value;
    }

    public int Value
    {
        get
        {
            return value;
        }
    }

    public void Change(int value)
    {
        this.value = value;
    }
}

MyStructを実装IChangeStructしているため、ボックス化されたコピーを、ボックス化を解除して新しいものに置き換えることなく、ヒープ内で直接変更できます。これは、次のコードで実証できます。

MyStruct[] l1 = new MyStruct[]
{
    new MyStruct(0)
};

Console.WriteLine(l1[0].Value); //0
l1[0].Change(10);
Console.WriteLine(l1[0].Value); //10

それでは、配列を に変更しましょうList<T>

List<MyStruct> l2 = new List<MyStruct>
{
    new MyStruct(0)
};

Console.WriteLine(l2[0].Value); //0
l2[0].Change(10);
Console.WriteLine(l2[0].Value); //also 0

私が理解している限り、最初のケースでl1[0]はボックス化された構造体への参照を返しましたが、2番目のケースではそうではありませんでした。

これも分解しようとしましたが、次のことがわかりました。

1) の場合MyStruct[]:

IL_0030:  ldelema    Utils.MyStruct
IL_0035:  ldc.i4.s   10
IL_0037:  call       instance void Utils.MyStruct::Change(int32)

2) の場合List<MyStruct>:

 IL_007c:  callvirt   instance !0 class [mscorlib]System.Collections.Generic.List`1<valuetype Utils.MyStruct>::get_Item(int32)
 IL_0081:  stloc.s    CS$0$0001
 IL_0083:  ldloca.s   CS$0$0001
 IL_0085:  ldc.i4.s   10
 IL_0087:  call       instance void Utils.MyStruct::Change(int32)

しかし、私はそれをうまく解釈する準備ができていないようでした。

それで、何をList<T>返しましたか?List<T>または、要素を配列してインデックスで返すにはどうすればよいでしょうか? それとも、これは値型の場合のみであり、参照型とは関係ありませんか?

PS:値型のインスタンスを変更してはならないこと理解していますが、説明されている問題により、配列がどのように機能するかを理解できませんでした。List<T>

4

2 に答える 2

9

.Net は、ldelema命令 (配列要素のロード アドレス) を使用して、配列要素をその場でアドレス指定できます。

これにより、配列要素をコピーせずに直接操作できます。ref(これは、配列要素をoroutパラメーターとして渡すことができる理由でもあります)

List<T>にはそのような能力はありません。代わりに、list[i]は単なる構文糖衣ですlist.get_Item(i)。これは、構造体のコピーを返す通常のメソッド呼び出しです。

于 2012-11-21T04:09:03.100 に答える
3

配列のインデクサーは、要素をパラメーターとして渡すのと同様の方法で、次のコードで使用できるようにしrefます。.net 言語には、他のタイプが同様に動作するメカニズムはありません。インデックス付きアクセスを許可するその他の型は、1 組のメソッドを公開する必要があります。そのうちの 1 つは内部に保存されたデータのコピーを呼び出し元のコードで使用できるようにし、もう 1 つは呼び出し元のコードからのデータのコピーを指定して保存します。そのデータを何らかの方法で。この制限は値型で最も顕著ですが、場合によっては参照型でも問題になる可能性があります (たとえば、 内の要素に対して を実行できますがInterlocked.ComapreExchange、 内の要素に対しては実行できT[]ませんList<T>)。

独自のコレクション型を設計している場合は、メンバーを提供することでインデクサーの制限を緩和し、 のActOnItemようなコードを許可できますMyFancyList.ActOnItem(4, (ref Point it) => {it.X += 4;});。このようなメソッドを使用すると、ラムダがキャプチャされた変数を使用する必要がなくなるためref、呼び出し元から渡されるさまざまな数の追加パラメーターを含む汎用バージョンのファミリーを提供すると役立つ場合があります(例: )。MyFancyList.ActOnItem(4, (ref MyThing it, ref Thing newValue, ref Thing compareValue) => Threading.Interlocked.CompareExchange(ref it, newValue, compareValue);

于 2012-11-21T18:11:37.637 に答える