2010-06-05 1 views
24

"참조"로 C# 클래스 필드에 할당하는 방법을 이해하려고합니다. 위의 코드를 실행하는 경우"참조"로 C#의 클래스 필드에 어떻게 할당합니까?

출력이
public class X 
{ 

    public X() 
    { 

    string example = "X"; 

    new Y(ref example); 

    new Z(ref example); 

    System.Diagnostics.Debug.WriteLine(example); 

    } 

} 

public class Y 
{ 

    public Y(ref string example) 
    { 
    example += " (Updated By Y)"; 
    } 

} 

public class Z 
{ 

    private string _Example; 

    public Z(ref string example) 
    { 

    this._Example = example; 

    this._Example += " (Updated By Z)"; 

    } 

} 

var x = new X(); 

:

X (Y하여 업데이트)

그리고하지 :

나는 고려하는 다음의 예를

X (업데이트 날짜 : Y) (Z 업데이트)

나는 기대했다.

"ref 매개 변수"를 필드에 할당하면 참조가 손실되는 것 같습니다.

필드에 지정할 때 참조를 유지할 수있는 방법이 있습니까?

감사합니다.

답변

8

참조 번호는 순전히 호출 규칙입니다. 필드를 한정하는 데 사용할 수 없습니다. Z에서 _Example은 전달 된 문자열 참조의 값으로 설정됩니다. 그런 다음 + =를 사용하여 새 문자열 참조를 할당합니다. 예제로 지정하지 않으므로 ref는 효과가 없습니다.

원하는 해결 방법은 참조 (문자열)가 포함 된 공유 가능한 가변 래퍼 객체 (배열 또는 가상 StringWrapper)를 사용하는 것뿐입니다. 일반적으로 이것을 필요로한다면 클래스가 공유 할 수있는 더 큰 변경 가능한 객체를 찾을 수 있습니다.

public class Z { 
    private string _Example; 

    public Z(ref string example) { 
     example = this._Example += " (Updated By Z)"; 
    } 
} 

출력 :

public class StringWrapper 
{ 
    public string s; 
    public StringWrapper(string s) 
    { 
    this.s = s; 
    } 

    public string ToString() 
    { 
    return s; 
    } 
} 

public class X 
{ 
    public X() 
    { 
    StringWrapper example = new StringWrapper("X"); 
    new Z(example) 
    System.Diagnostics.Debug.WriteLine(example); 
    } 
} 

public class Z 
{ 
    private StringWrapper _Example; 
    public Z(StringWrapper example) 
    { 
    this._Example = example; 
    this._Example.s += " (Updated By Z)"; 
    } 
} 
+0

두 번째 단락을 약간 확장 하시겠습니까? "더 큰 변경 가능 객체"는 완전히 명확하지 않습니다. 또한 필드에서 문자열 참조를 유지할 방법이 없다면 StringWrapper가 어떻게 작동합니까? –

+0

나는 명확히해야 할 예를 제시했다. 제가 말했듯이, 실제 디자인에서 StringWrapper는 단순한 문자열 이상을 담고있는 비즈니스 객체 일 것입니다. –

+0

아이디어를 제공해 주셔서 감사합니다. 내 예제는 실제로 단순화 된 것입니다. 왜냐하면 실제로 참조 할 수 있도록 할당하려고하는 실제 코드에 객체 (문자열이 아님)가 있기 때문입니다. 내가 찾고있는 동작은 객체의 사용자가 객체에 null을 할당 할 수 있도록 허용하거나 객체의 메소드에서 해당 유형의 새 인스턴스를 할당하는 것입니다. 일부 의존성 삽입의 일부로 생성자를 통해 객체를 전달하므로 다른 메소드는 필드에 액세스하여 객체를 볼 수 있습니다. 부끄러운 일은 내가 원하는대로 할 수없는 것처럼 보입니다. – Jamie

3

당신은 Z 클래스에서 참조 업데이트 깜빡 염두에두고 (Z에 의해 업데이트)

포인트 (Y하여 업데이트) X를 그입니다 문자열에 대한 + = 연산자는 String.Concat() 메서드를 호출합니다. 어떤 새로운 문자열 개체를 만듭니다, 그것은 문자열 값을 업데이 트하지 않습니다. String 객체는 변경할 수 없으며 문자열 클래스에는 값을 변경할 수있는 메서드 나 필드가 없습니다. 정규 참조 유형의 기본 동작과 매우 다릅니다.

문자열 메서드 또는 연산자를 사용하는 경우 항상 반환 값을 변수에 다시 지정해야합니다. 이것은 매우 자연스러운 구문이며 값 유형은 동일한 방식으로 작동합니다. 문자열 대신 int를 사용하면 코드가 매우 유사합니다.

+1

한스에게 감사드립니다. 한스. 내 예제가 잘못되었습니다. 간단하게 유지하기 위해 생성자에서 업데이트했습니다. 실제로 필드는 생성자에서 설정되지만 객체가 인스턴스화 된 후 다른 메소드에서 업데이트가 발생합니다. – Jamie

56

다른 사람이 지적했듯이 "ref to variable"유형의 필드를 가질 수 없습니다. 그러나 당신이 그것을 할 수 없다는 것을 아는 것은 아마도 불만족 스러울 것입니다; 당신은 아마 또한 첫 번째, 왜, 그리고 두 번째,이 제한을 극복하는 방법을 알고 싶다.

이유 이유는 단지 세 가지 가능성이 있기 때문에입니다 :

1) REF 유형의 안전하지 않은 필드

3) 임시 저장소를 사용하지 마십시오 허용

2) 심판 유형의 필드를 허용 안가 지역 변수를위한 풀 (일명 "스택")

ref 타입의 필드를 허용한다고 가정 해 봅시다. 그럼 당신은 할 수있어

public ref int x; 
void M() 
{ 
    int y = 123; 
    this.x = ref y; 
} 

M 완료 후 y를 액세스 할 수 있습니다. 즉, y에 대한 저장 공간이 더 이상 존재하지 않기 때문에 (2) - this.x에 액세스하면 크래시가 발생하고 끔찍하게 죽을 수도 있습니다. 또는 우리는 (3)의 경우이므로 로컬 y이 가비지에 저장됩니다 임시 메모리 풀이 아니라 수집 된 힙.

지역 변수가 ref로 전달되는 경우에도 로컬 변수가 임시 풀에 저장되는 최적화가 마음에 들지만 프로그램을 중단하고 나중에 죽일 수있는 시간 폭탄을 남길 수 있다는 생각이 싫습니다. 따라서 옵션 1 : ref 필드가 없습니다.

익명 함수의 폐쇄 변수 인 지역 변수에 대해서는 옵션 (3)을 선택합니다. 이러한 로컬 변수는 임시 풀에서 할당되지 않습니다.

그런 다음 두 번째 질문으로 안내합니다. 어떻게 해결할 수 있습니까? ref 필드가 다른 변수의 getter와 setter를 만드는 것이면 완벽하게 합법적입니다.

sealed class Ref<T> 
{ 
    private readonly Func<T> getter; 
    private readonly Action<T> setter; 
    public Ref(Func<T> getter, Action<T> setter) 
    { 
     this.getter = getter; 
     this.setter = setter; 
    } 
    public T Value { get { return getter(); } set { setter(value); } } 
} 
... 
Ref<int> x; 
void M() 
{ 
    int y = 123; 
    x = new Ref<int>(()=>y, z=>{y=z;}); 
    x.Value = 456; 
    Console.WriteLine(y); // 456 -- setting x.Value changes y. 
} 

그리고 거기에 가야합니다. y은 gc 힙에 저장되고 xy을 가져오고 설정할 수있는 개체입니다.

CLR은 참조 지역과 참조 반환 방법을 지원하지만 C#은 지원하지 않습니다. 아마도 C#의 가상 버전이 이러한 기능을 지원할 것입니다. 프로토 타입을 작성 했으므로 제대로 작동합니다. 그러나 이것은 우선 순위 목록에 실재하지 않습니다. 그래서 저는 희망을 갖지 않을 것입니다.

업데이트 : 위의 단락에서 언급 한 기능이 C# 7에서 실제로 구현되었습니다. 그러나 여전히 필드에는 ref를 저장할 수 없습니다.

+0

getter와 setter가 필요한 이유 - y를 boxed 상태로 유지하는 것이 중요합니까?단순히 Ref를 사용할 수 있습니까 {public T Value {get; 세트; }} 대신? –

+1

@ChrisMoschini : 물론 가능합니다. 하지만 변수 *가 아니라 값 *에 대한 참조를 전달하고 있습니다. 질문의 요점은 "변수에 대한 참조를 어떻게 저장합니까?"입니다. 지역 변수 y 대신 배열의 12 번째 요소에 "참조"를 저장하여 참조를 변경하면 배열의 12 번째 요소가 변형되도록합니다. 이 시나리오에서는 제안 된 유형이 도움이되지 않습니다. –

+0

사실, 둘 다 .someMethod (ref 배열 [12])를 전달하지 않지만 질문의 정신보다 더 많은 것으로 보인다. 우리 제안 중 어느 것도 당신에게 가치가있는 유형을 얻지 못했고 불행히도 박스형으로 남아 있습니다. Nullable 은 실제로 묻는 사람이 시도한 것과 가장 일치 할 수 있습니다. –

관련 문제