2012-11-21 4 views
6

Is it possible to access a reference of a struct from a List to make changes? 스레드가 리자에 의해 내 마음에 제기 된 질문.리스트 <T>과 배열 인덱서의 차이점은 무엇입니까?

그래서, 고려 structinterface (매우 유용하지만, 단지 문제를 표시하지 않도록 definetely) 다음

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; 
    } 
} 

MyStructIChangeStruct 구현, 그래서 우리는 바로 힙에서의 박스 사본을 변경할 수 있습니다 언 박싱 및 새 것으로 교체하지 않아도됩니다. 이것은 다음과 같은 코드로 demostrated 할 수있다 : 지금까지 내가 이해로

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]의 박스에 referense을 반환

이제
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>로 배열을 변경할 수 있습니다 구조체, 반면에 두 번째 - 그 밖의 smth했다.

나는 또한이 분해 시도하고 발견

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> 요소를 인덱스로 반환하는 방법은 무엇입니까? 또는 값 유형이있는 유일한 경우이며 참조 유형과 관련이 없습니까?

P.S는 : 나는 한 되지해야 변화 값 유형 인스턴스하지만, 기술 문제가 나를 이해하게 이해 할 , 나는 결코 실현되지 방법 List<T> 및 배열 작업.

답변

9

.Net은 ldelema 명령어 (배열 요소의로드 주소)를 사용하여 배열 요소를 내부적으로 처리 할 수 ​​있습니다.

이렇게하면 배열 요소를 복사하지 않고 직접 조작 할 수 있습니다. (배열 요소를 ref 또는 out 매개 변수로 전달할 수도 있습니다)

List<T>에는 이러한 기능이 없습니다. 대신 list[i]list.get_Item(i)에 대한 구문 설탕이며 구조체의 복사본을 반환하는 정상적인 메서드 호출입니다.

+1

어레이는 인덱스가 0이고 1 차원 인 경우에만이 기능을 사용할 수 있습니까? ('ldelema'는 그 제한 하에서 만 작동 할 수 있기 때문에) – Earlz

3

배열의 인덱서는 ref 매개 변수로 전달하는 것과 유사한 방식으로 다음 코드에서 요소를 사용할 수있게합니다. 비슷한 유형의 다른 모든 유형의 .net 언어에는 어떤 메커니즘도 존재하지 않습니다. 인덱싱 된 액세스를 허용하는 다른 유형은 메소드 쌍을 노출해야하며, 그 중 하나는 내부 저장된 데이터의 복사본을 호출자의 코드에서 사용할 수있게하며, 그 중 하나는 호출자의 코드에서 일부 데이터의 복사본이 저장되면 그 데이터를 어떤 방식 으로든이 제한은 값 유형에서 가장 많이 볼 수 있지만 경우에 따라 참조 유형에 문제가있을 수 있습니다 (예 : T[]의 요소에는 Interlocked.ComapreExchange을 수행 할 수 있지만 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);).

관련 문제