.NET에서 문자열은 어떻게 전달됩니까?


121

string함수에 a 를 전달할 때 문자열의 내용에 대한 포인터가 전달 struct됩니까? 아니면 전체 문자열이 a와 같이 스택의 함수에 전달 됩니까?

답변:


278

참조가 전달됩니다. 그러나 기술적 으로 참조로 전달 되지는 않습니다 . 이것은 미묘하지만 매우 중요한 차이입니다. 다음 코드를 고려하십시오.

void DoSomething(string strLocal)
{
    strLocal = "local";
}
void Main()
{
    string strMain = "main";
    DoSomething(strMain);
    Console.WriteLine(strMain); // What gets printed?
}

여기서 일어나는 일을 이해하기 위해 알아야 할 세 가지가 있습니다.

  1. 문자열은 C #의 참조 유형입니다.
  2. 그들은 또한 불변이므로 문자열을 변경하는 것처럼 보이는 작업을 할 때마다 그렇지 않습니다. 완전히 새로운 문자열이 생성되고 참조가 해당 문자열을 가리키며 이전 문자열이 버려집니다.
  3. 문자열은 참조 유형이지만 참조로 strMain전달되지 않습니다. 그것은 A의 참조 형, 하지만 기준 자체입니다 값에 의해 전달 . 매개 변수를 ref계산 하지 않고 키워드 없이 매개 변수를 전달할 때마다 out값으로 무언가를 전달한 것입니다.

그래서 그것은 당신이 가치로 참조를 전달하고 있다는 것을 의미합니다. 참조 유형이므로 참조 만 스택에 복사되었습니다. 그러나 그것은 무엇을 의미합니까?

값으로 참조 유형 전달 : 이미 수행 중입니다.

C # 변수는 참조 유형 또는 값 유형 입니다. C # 매개 변수는 참조로 전달 되거나 전달 value로 전달됩니다 . 여기서 용어는 문제입니다. 이것들은 똑같이 들리지만 그렇지 않습니다.

ANY 유형의 매개 변수를 전달하고 ref키워드를 사용하지 않으면 값으로 전달한 것입니다. 가치로 전달했다면 실제로 전달한 것은 사본이었습니다. 그러나 매개 변수가 참조 유형이면 복사 한 것은 참조가 아닌 참조 입니다.

다음은 Main메서드 의 첫 번째 줄입니다 .

string strMain = "main";

우리는이 줄에 두 가지를 만들었습니다 : 값이 main어딘가에 메모리에 저장되어 있는 문자열과 strMain그것을 가리키는 참조 변수 입니다.

DoSomething(strMain);

이제 해당 참조를 DoSomething. 우리는 그것을 가치로 전달했기 때문에 우리가 복사본을 만들었다는 것을 의미합니다. 참조 유형이므로 문자열 자체가 아니라 참조를 복사했음을 의미합니다. 이제 각각 메모리에서 동일한 값을 가리키는 두 개의 참조가 있습니다.

수신자 내부

DoSomething방법 의 상단은 다음과 같습니다 .

void DoSomething(string strLocal)

어떤 ref키워드, 그래서 strLocalstrMain같은 값을 가리키는 두 개의 서로 다른 언급은 없다. 재 할당하면 strLocal...

strLocal = "local";   

... 저장된 값을 변경하지 않았습니다. 우리는라는 참조를 가져 와서 strLocal새로운 문자열을 겨냥했습니다. 무슨 일이 일어나는 strMain우리가 그렇게 할 때? 아무것도. 여전히 이전 문자열을 가리키고 있습니다.

string strMain = "main";    // Store a string, create a reference to it
DoSomething(strMain);       // Reference gets copied, copy gets re-pointed
Console.WriteLine(strMain); // The original string is still "main" 

불변성

잠시 시나리오를 변경해 보겠습니다. 우리가 문자열로 작업하지 않고 생성 한 클래스와 같은 변경 가능한 참조 유형을 사용한다고 상상해보십시오.

class MutableThing
{
    public int ChangeMe { get; set; }
}

당신은 경우 에 따라 기준 objLocal이, 당신은 그것의 속성을 변경할 수 있습니다 가리키는 객체를 :

void DoSomething(MutableThing objLocal)
{
     objLocal.ChangeMe = 0;
} 

MutableThing메모리 에는 여전히 하나만 있고 복사 된 참조와 원래 참조가 여전히 참조를 가리 킵니다. 자체 속성 MutableThing이 변경되었습니다 .

void Main()
{
    var objMain = new MutableThing();
    objMain.ChangeMe = 5; 
    Console.WriteLine(objMain.ChangeMe); // it's 5 on objMain

    DoSomething(objMain);                // now it's 0 on objLocal
    Console.WriteLine(objMain.ChangeMe); // it's also 0 on objMain   
}

아,하지만 문자열은 불변입니다! ChangeMe설정할 속성 이 없습니다 . strLocal[3] = 'H'C 스타일 char배열 에서 할 수있는 것처럼 C #에서는 할 수 없습니다 . 대신 완전히 새로운 문자열을 구성해야합니다. 변화에 대한 유일한 방법은 strLocal다른 문자열에 대한 참조를 가리 키도록하고, 수단 아무것도 것을 당신은 위해 할 strLocal에 영향을 줄 수 있습니다 strMain. 값은 변경할 수 없으며 참조는 사본입니다.

참조로 참조 전달

차이점이 있음을 증명하기 위해 참조로 참조를 전달하면 다음과 같은 결과가 발생합니다.

void DoSomethingByReference(ref string strLocal)
{
    strLocal = "local";
}
void Main()
{
    string strMain = "main";
    DoSomethingByReference(ref strMain);
    Console.WriteLine(strMain);          // Prints "local"
}

이번에 Main는 참조를 스택에 복사하지 않고 전달했기 때문에 의 문자열이 실제로 변경됩니다.

따라서 문자열이 참조 유형이더라도 값으로 전달하면 호출 수신자에서 진행되는 모든 작업이 호출자의 문자열에 영향을주지 않습니다. 그러나 그것들 참조 유형 이기 때문에 전달하고자 할 때 전체 문자열을 메모리에 복사 할 필요가 없습니다.

추가 리소스 :


3
@TheLight-죄송합니다. "기본적으로 참조 유형은 참조로 전달됩니다."라고 말하면 여기에서 틀립니다. 기본적으로 모든 매개 변수는 값으로 전달되지만 참조 유형을 사용 하면 참조가 값으로 전달됨을 의미합니다. 참조 유형을 참조 매개 변수와 결합하고 있는데, 이는 매우 혼란스러운 구별이기 때문에 이해할 수 있습니다. 참고 항목 여기에 값 섹션에 의해 전달 참조 유형. 귀하의 링크 된 기사는 매우 정확하지만 실제로 내 요점을 지원합니다.
Justin Morgan

1
@JustinMorgan 죽은 댓글 스레드를 가져 오지는 않지만 C로 생각하면 TheLight의 댓글이 합리적이라고 생각합니다. C에서 데이터는 메모리 블록 일뿐입니다. 참조는 해당 메모리 블록에 대한 포인터입니다. 전체 메모리 블록을 함수에 전달하는 것을 "값으로 전달"이라고합니다. 포인터를 전달하면 "참조로 전달"이라고합니다. C #에서는 전체 메모리 블록을 전달하는 개념이 없으므로 "값으로 전달"을 다시 정의하여 포인터를 전달하는 것을 의미합니다. 잘못된 것 같지만 포인터도 메모리 블록 일뿐입니다! 나를 위해, 용어는 꽤 임의
rliu는

@roliu-문제는 우리가 C에서 작업하지 않는다는 것입니다. 그리고 C #은 비슷한 이름과 구문에도 불구하고 매우 다릅니다. 한 가지로, 참조는 포인터와 같지 않으며 그렇게 생각하면 함정으로 이어질 수 있습니다. 그러나 가장 큰 문제는 "참조로 전달"이 C #에서 매우 구체적인 의미를 가지므로 ref키워드가 필요하다는 것 입니다. 참조로 전달하면 차이가 있음을 증명하려면 다음 데모를 참조하십시오. rextester.com/WKBG5978
Justin Morgan

1
@JustinMorgan 나는 C와 C # 용어를 혼합하는 것이 나쁘다는 데 동의하지만 Lippert의 게시물을 즐겼지만 참조를 포인터로 생각하는 것이 특히 여기에서 무엇이든 안개가 낀다는 데 동의하지 않습니다. 블로그 게시물은 참조를 포인터로 생각하는 것이 어떻게 너무 많은 힘을 주는지 설명합니다. ref키워드에 유틸리티가 있다는 것을 알고 있습니다 . C #에서 값으로 참조 유형을 전달하는 것이 참조로 전달하고 참조 유형을 전달하는 "전통적인"(예 : C) 개념처럼 보이는 이유를 설명하려고했습니다. 참조로 C #에서 참조에 대한 참조를 값으로 전달하는 것과 비슷합니다.
rliu

2
당신이 올바른지,하지만 난 @roliu 함수 등이 방법 참조 된 생각 Foo(string bar)으로 생각 될 수있는 Foo(char* bar)반면 Foo(ref string bar)될 것이다 Foo(char** bar)(또는 Foo(char*& bar)또는 Foo(string& bar)C ++에서). 물론, 그것은 당신이 그것을 매일 어떻게 생각해야 하는가는 아니지만 실제로는 내부에서 일어나는 일을 마침내 이해하는 데 도움이되었습니다.
Cole Johnson

23

C #의 문자열은 변경할 수없는 참조 개체입니다. 이것은 그들에 대한 참조가 (값으로) 전달되고 문자열이 생성되면 수정할 수 없음을 의미합니다. 수정 된 버전의 문자열 (하위 문자열, 잘린 버전 등)을 생성하는 메서드 는 원래 문자열의 수정 된 복사본 을 만듭니다 .


10

문자열은 특별한 경우입니다. 각 인스턴스는 변경할 수 없습니다. 문자열의 값을 변경하면 메모리에 새 문자열이 할당됩니다.

따라서 참조 만 함수에 전달되지만 문자열이 편집되면 새 인스턴스가되고 이전 인스턴스를 수정하지 않습니다.


4
이 측면에서 문자열은 특별한 경우 가 아닙니다 . 동일한 의미를 가질 수있는 불변 객체를 만드는 것은 매우 쉽습니다. (즉, 변경하는 메서드를 노출하지 않는 유형의 인스턴스 ...)

문자열은 특수한 경우입니다. 값 유형처럼 작동한다는 점에서 변경 가능한 것처럼 보이는 사실상 변경 불가능한 참조 유형입니다.
Enigmativity

1
@Enigmativity 그 논리에 의해 Uri(클래스)와 Guid(구조체)도 특별한 경우입니다. System.String클래스 또는 구조체 기원의 다른 불변 유형보다 "값 유형"처럼 작동 하는 방식을 알지 못합니다 .

3
@pst-문자열에는 Uri& 와 달리 특별한 생성 의미가 Guid있습니다. 문자열 변수에 문자열 리터럴 값을 할당 할 수 있습니다. 문자열 int은 재 할당되는 것처럼 변경 가능한 것처럼 보이지만 암시 적으로 객체를 생성합니다 ( new키워드 없음) .
Enigmativity

3
문자열은 특별한 경우이지만이 질문과는 관련이 없습니다. 값 유형, 참조 유형, 모든 유형이이 질문에서 모두 동일하게 작동합니다.
Kirk Broadhurst
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.