문제

구조체 목록이 있는데 하나의 요소를 변경하고 싶습니다.예를 들어 :

MyList.Add(new MyStruct("john");
MyList.Add(new MyStruct("peter");

이제 한 요소를 변경하고 싶습니다.

MyList[1].Name = "bob"

그러나 이 작업을 시도할 때마다 다음 오류가 발생합니다.

system.collections.generic.list.this [int]의 반환 값을 변수가 아니기 때문에 수정할 수 없습니다.

클래스 목록을 사용하면 문제가 발생하지 않습니다.

대답은 구조체가 값 유형이라는 것과 관련이 있다고 생각합니다.

따라서 구조체 목록이 있는 경우 이를 다음과 같이 처리해야 합니다. 읽기 전용?목록의 요소를 변경해야 한다면 구조체가 아닌 클래스를 사용해야 합니까?

도움이 되었습니까?

해결책

MyList[1] = new MyStruct("bob");

C#의 구조체는 거의 항상 변경할 수 없도록 설계되어야 합니다. 즉, 일단 생성되면 내부 상태를 변경할 수 없습니다.

귀하의 경우 원하는 것은 단일 속성이나 필드만 변경하는 것이 아니라 지정된 배열 인덱스의 전체 구조체를 바꾸는 것입니다.

다른 팁

좀 빠지는.유형을 클래스 또는 구조체로 디자인하는 것은 컬렉션에 저장해야 한다는 요구에 의해 추진되어서는 안 됩니다. :) 필요한 '의미'를 살펴봐야 합니다.

보고 있는 문제는 값 유형 의미 때문에 발생합니다.각 값 유형 변수/참조는 새로운 인스턴스입니다.네가 말할 때

Struct obItem = MyList[1];

그러면 구조체의 새 인스턴스가 생성되고 모든 멤버가 하나씩 복사됩니다.MyList[1]의 복제본을 갖게 됩니다. 즉,2개의 인스턴스.이제 obItem을 수정해도 원본에는 영향을 주지 않습니다.

obItem.Name = "Gishu";  // MyList[1].Name still remains "peter"

이제 여기서 2분만 참아주세요(꿀꺽 삼키는 데 시간이 좀 걸립니다..그것은 나를 위해했다 :) 당신이 실제로 컬렉션에 저장하고 질문에 표시된 것처럼 수정해야한다면, 구조물 노출 인터페이스를 만들어야한다 (그러나 이로 인해 권투가 발생합니다.).그런 다음 박스형 개체를 참조하는 인터페이스 참조를 통해 실제 구조체를 수정할 수 있습니다.

다음 코드 조각은 위에서 말한 내용을 보여줍니다.

public interface IMyStructModifier
{
    String Name { set; }
}
public struct MyStruct : IMyStructModifier ...

List<Object> obList = new List<object>();
obList.Add(new MyStruct("ABC"));
obList.Add(new MyStruct("DEF"));

MyStruct temp = (MyStruct)obList[1];
temp.Name = "Gishu";
foreach (MyStruct s in obList) // => "ABC", "DEF"
{
    Console.WriteLine(s.Name);
}

IMyStructModifier temp2 = obList[1] as IMyStructModifier;
temp2.Name = "Now Gishu";
foreach (MyStruct s in obList) // => "ABC", "Now Gishu"
{
    Console.WriteLine(s.Name);
}

HTH.좋은 질문.
업데이트: @Hath - 내가 그렇게 간단한 것을 간과했는지 확인하기 위해 달려가게 하셨습니다.(세터 속성과 메소드가 일치하지 않으면 일관성이 없습니다. .Net 세계는 여전히 균형을 이루고 있습니다.)
세터 메소드가 작동하지 않습니다
obList2[1]은 상태가 수정될 복사본을 반환합니다.목록의 원래 구조체는 수정되지 않은 상태로 유지됩니다.따라서 Set-via-Interface가 이를 수행하는 유일한 방법인 것 같습니다.

List<MyStruct> obList2 = new List<MyStruct>();
obList2.Add(new MyStruct("ABC"));
obList2.Add(new MyStruct("DEF"));
obList2[1].SetName("WTH");
foreach (MyStruct s in obList2) // => "ABC", "DEF"
{
    Console.WriteLine(s.Name);
}

구조체가 "불변"이라는 것은 그다지 중요하지 않습니다.

실제 근본적인 문제는 구조체가 참조 유형이 아닌 값 유형이라는 것입니다.따라서 목록에서 구조체에 대한 "참조"를 꺼내면 전체 구조체의 새 복사본이 생성됩니다.따라서 이를 변경하면 목록의 원본 버전이 아닌 복사본이 변경됩니다.

Andrew가 말한 것처럼 전체 구조체를 교체해야 합니다.하지만 그 시점에서는 왜 처음에 (클래스 대신) 구조체를 사용하고 있는지 스스로에게 물어봐야 한다고 생각합니다.조기 최적화 문제를 해결하지 않도록 하세요.

필드를 노출했거나 속성 설정자를 통해 변형을 허용하는 구조체에는 아무런 문제가 없습니다.그러나 메서드나 속성 getter에 대한 응답으로 자체적으로 변형되는 구조체는 시스템에서 임시 구조체 인스턴스에 대해 메서드나 속성 getter가 호출되도록 허용하므로 위험합니다.메서드나 getter가 구조체를 변경하면 해당 변경 사항은 결국 삭제됩니다.

안타깝게도 .net에 내장된 컬렉션은 그 안에 포함된 값 유형 개체를 노출하는 데 실제로 취약합니다.가장 좋은 방법은 일반적으로 다음과 같은 작업을 수행하는 것입니다.

  MyStruct temp = myList[1];
  temp.Name = "Albert";
  myList[1] = temp;

다소 짜증나고 스레드로부터 전혀 안전하지 않습니다.동일한 작업을 수행하려면 다음이 필요할 수 있는 클래스 유형 목록에 비해 여전히 개선되었습니다.

  myList[1].Name = "Albert";

그러나 다음이 필요할 수도 있습니다.

  myList[1] = myList[1].Withname("Albert");

아니면 어쩌면

  myClass temp = (myClass)myList[1].Clone();
  temp.Name = "Albert";
  myList[1] = temp;

아니면 다른 변형일 수도 있습니다.myClass와 목록에 항목을 넣는 다른 코드를 검사하지 않으면 실제로 알 수 없습니다.액세스 권한이 없는 어셈블리의 코드를 검사하지 않으면 첫 번째 양식이 안전한지 여부를 알 수 없을 수도 있습니다.대조적으로, Name이 MyStruct의 노출된 필드인 경우 업데이트를 위해 제공한 방법은 MyStruct에 포함된 다른 내용이나 코드가 실행되기 전에 myList를 사용하여 수행한 다른 작업 또는 예상되는 작업에 관계없이 작동합니다. 그 후에 하세요.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top