2013-08-17 3 views
0

좋아, 내가 변경할 수있는 구조체 뒤에있는 악의를 이해한다고 말하는 것부터 시작하겠습니다.하지만 SFML.net과 많은 Vector2f 및 구조체를 사용하고 있습니다.속성으로 필드를 액세스하고 구조체를 필드로 변경하는 것

내가 얻지 못하는 이유는 클래스의 필드를 가질 수 있고 값을 변경 할 수 있으며 동일한 클래스에서 속성을 사용하여 동일한 작업을 수행 할 수없는 이유입니다.

이 코드를 살펴 보자 :

using System; 

namespace Test 
{ 
    public struct TestStruct 
    { 
     public string Value; 
    } 

    class Program 
    { 
     TestStruct structA; 
     TestStruct structB { get; set; } 

     static void Main(string[] args) 
     { 
      Program program = new Program(); 

      // This Works 
      program.structA.Value = "Test A"; 

      // This fails with the following error: 
      // Cannot modify the return value of 'Test.Program.structB' 
      // because it is not a variable 
      //program.structB.Value = "Test B"; 

      TestStruct copy = program.structB; 
      copy.Value = "Test B"; 

      Console.WriteLine(program.structA.Value); // "Test A" 
      Console.WriteLine(program.structB.Value); // Empty, as expected 
     } 
    } 
} 

참고 : 나는 같은 기능을 포함하고 내 가변성으로 유지하기 위해 내 자신의 클래스를 만들 수 있습니다,하지만 난 왜 내가 할 수있는 기술적 인 이유를 볼 수 없습니다 하나를하고 다른 것을 할 수는 없다.

답변

4

필드에 액세스하면 실제 구조체에 액세스하고 있습니다. 속성을 통해 액세스하면 속성에 저장된 내용을 반환하는 메서드를 호출합니다. 값 유형 인 구조체의 경우 구조체의 사본을 얻습니다. 분명히 그 복사본은 변수가 아니며 변경할 수 없습니다.

부분은 C# 언어 사양 5.0 "1.7 구조체"라고 :

클래스와

두 변수가 영향을 미치는 하나 개의 변수에 동일한 객체를 참조 및 조작에 대한 것이 가능하는 것은 가능 다른 변수에 의해 참조되는 객체. 구조체를 사용하면 변수 에 각각 자체 데이터 복사본이 있으며, 하나에 대한 조작이 다른 하나에 영향을 줄 수는 없습니다.

그러면 구조체의 복사본을 받고 원본 구조체를 수정할 수 없다고 설명합니다. 그러나 허용되지 않는 이유는 설명하지 않습니다. "절 11.3.3의"specifcation의

: 구조체의 속성이나 인덱서 할당의 대상

이며, 속성 또는 인덱서 액세스 와 연관된 인스턴스 식 분류되어야 변수로. 인스턴스 표현식이 값으로 분류 된 이면 컴파일 타임 오류가 발생합니다. 이것은 §7.17.1에서 더 자세히 설명 되어진 으로 설명됩니다.

따라서 get 접근 자에서 반환되는 "물건"은 변수이며 값이 아닙니다. 그것은 오류 메시지의 문구를 설명합니다.

사양은 또한 당신의 코드와 거의 동일 섹션 7.17.1의 예를 포함

: 선언

감안할 때 : 예에서

struct Point 
{ 
    int x, y; 
    public Point(int x, int y) { 
     this.x = x; 
     this.y = y; 
    } 
    public int X { 
     get { return x; } 
     set { x = value; } 
    } 
    public int Y { 
     get { return y; } 
     set { y = value; } 
    } 
} 
struct Rectangle 
{ 
    Point a, b; 
    public Rectangle(Point a, Point b) { 
     this.a = a; 
     this.b = b; 
    } 
    public Point A { 
     get { return a; } 
     set { a = value; } 
    } 
    public Point B { 
     get { return b; } 
     set { b = value; } 
    } 
} 

Point p = new Point(); 
p.X = 100; 
p.Y = 100; 
Rectangle r = new Rectangle(); 
r.A = new Point(10, 10); 
r.B = p; 

할당을 p와 r은 변수이므로 pX, pY, rA 및 rB는 허용됩니다. 그러나 예에서

Rectangle r = new Rectangle(); 
r.A.X = 10; 
r.A.Y = 10; 
r.B.X = 100; 
r.B.Y = 100; 

r.A 및 r.B는 변수가 아니기 때문에 할당이 모두 유효하지 않습니다.

+0

감사합니다. Abel. 귀하의 설명 (값을 반환하는 것은 그것을 복사하는 것과 동일합니다)은 내가 찾고있는 정확한 부분입니다. – NemoStein

2

속성은 변수처럼 보이지만 각 속성은 실제로 get 메서드 및/또는 set 메서드의 조합입니다. 통상, 프로퍼티 get 메소드는, 변수 또는 배열 슬롯에있는 것을 카피합니다. put 메소드는, 그 파라미터를 그 ​​변수 또는 배열 슬롯에 카피합니다. someVariable = someObject.someProeprty; 또는 someobject.someProperty = someVariable;과 같은 작업을 수행하려는 경우 해당 문구가 각각 var temp=someObject.somePropertyBackingField; someVariable=temp;var temp=someVariable; someObject.somePropertyBackingField=temp;으로 실행되는 것은 중요하지 않습니다. 반면에 필드를 사용하여 수행 할 수 있지만 속성을 사용하여 수행 할 수없는 작업이 있습니다. 객체가 GeorgeField1라는 필드를 노출하면

다음 코드는 다른 방법으로 또는 refoutGeorge.Field 파라미터로서 전달할 수있다. 또한 Field1 유형이 노출 된 필드가있는 값 유형 인 경우 해당 필드에 액세스하려는 시도는 George에 저장된 구조체의 필드에 액세스합니다. Field1에 속성이나 메서드가 공개되어있는 경우 해당 개체에 액세스하면 ref 매개 변수 인 것처럼 George.Field1이 해당 메서드로 전달됩니다. George 만약

는 "GET"메서드를 호출하고 임시 변수에 그 결과를 저장합니다 할당 연산자의 왼쪽 아니다 Property1이라는 속성, Property1의 다음 액세스를 제공합니다. Property1 필드를 읽으 려하면 임시 변수에서 해당 필드를 읽습니다. Property1에서 속성 가져 오기 도구 또는 메서드를 호출하면 해당 임시 변수가 해당 메서드에 ref 매개 변수로 전달 된 다음 메서드가 반환 된 후 삭제됩니다. 메서드 또는 속성 getter 또는 메서드 내에서 this은 임시 변수를 참조하고 메서드가 변경 한 내용은 this으로 삭제됩니다.

임시 변수의 필드에 쓰는 것은 의미가 없기 때문에 속성 필드에 쓰려고 시도 할 수 없습니다. 또한 현재 버전의 C# 컴파일러는 속성 설정자가 this을 수정할 가능성이 높으며 따라서 실제로 기본 구조를 수정하지 않더라도 속성 설정자를 사용하지 못하도록합니다. 그 이유는 ArraySegment은 인덱스 된 get 방법을 포함하고 인덱스 된 set 방법을 포함하지 않기 때문에, 예를 들면, thing.theArraySegment[3] = 4; 컴파일러는 참조가 캡슐화 된 배열을 수정하는 대신 theArraySegment 속성에 의해 반환 된 구조를 수정하려고 시도했다고 생각합니다. 특정 구조 메서드가 this을 수정할 것이라고 지정하고 구조체 속성에서 호출 할 수 없도록 지정하는 것이 매우 유용 할 수 있지만 아직 그 메커니즘은 없습니다. 하나의 속성에 포함 된 필드에 기록하기를 원한다면

, 최적의 패턴은 일반적으로 : myProperty의 유형이 관련되지만 독립적 인 값의 고정 세트를 캡슐화하기 위해 설계된 경우

var temp = myThing.myProperty; // Assume `temp` is a coordinate-point structure 
temp.X += 5; 
myThing.myProperty = temp; 

(예 : 점의 좌표), 변수를 필드로 노출하는 것이 가장 좋습니다. 어떤 사람들은 같은 구조가 필요하기 때문에 구조체를 설계하는 것을 선호하는 것 같다 있지만 :

var temp = myThing.myProperty; // Assume `temp` is some kind of XNA Point structure 
myThing.myProperty = new CoordinatePoint(temp.X+5, temp.Y); 

을 내가 덜 읽을 덜 효율적인, 그리고 더 많은 오류가 발생하기 쉬운 이전의 스타일보다는 같은 코드를 생각한다. 특히 CoordinatePoint이 발생하면 예 : 매개 변수 X, Y, Z뿐만 아니라 매개 변수 X, Y를 취하고 Z가 0이라고 가정하는 생성자를 노출합니다. 두 번째 형식과 같은 코드는 의도적으로 또는 의도하지 않게 Z를 0으로 만들지 않습니다.반대로 X이 노출 된 필드 인 경우 첫 번째 양식은 수정 X 일 것입니다.

경우에 따라 클래스가 내부 필드 또는 배열 슬롯을 사용자 정의 루틴에 ref 매개 변수로 전달하는 메소드를 통해 노출하는 것이 도움이 될 수 있습니다. List<T> -like 클래스는 노출 될이 그

myList.ActOnItem(5, (ref Point pt, ref int ddx) => pt.X += ddx, ref dx); 

참고 : 다음 할 수있는 FancyList<CoordinatePoint>을했고

delegate void ActByRef<T1>(ref T1 p1); 
delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2); 

void ActOnItem(int index, ActByRef<T> proc) 
{ 
    proc(ref BackingArray[index]); 
} 
void ActOnItem<PT>(int index, ActByRef<T,PT> proc, ref PT extraParam) 
{ 
    proc(ref BackingArray[index], ref extraParam); 
} 

코드 IIT에서 항목 5의 X를 필드 일부 지역 변수 dx을 추가하고 싶었 접근법은 목록에있는 데이터의 적절한 수정을 허용하고 심지어 Interlocked.CompareExchange과 같은 방법의 사용을 허용합니다. 불행히도 List<T>에서 파생 된 형식이 이러한 메서드를 지원할 수있는 메커니즘이 없으며 이러한 메서드를 지원하는 형식을 List<T>이 필요한 코드로 전달할 수있는 메커니즘이 없습니다.

+0

많은 감사, Supercat ... 나는 당신의 대답을 받아 들일 것이지만, 나는 오직 1을 받아 들일 수 있고, Anders Abel은 당신보다 조금 빠르지 만, 당신의 텍스트는 주목할 만하다. – NemoStein

관련 문제