[C#] C # 문자열 참조 유형?

C #의 “string”이 참조 유형이라는 것을 알고 있습니다. 이것은 MSDN에 있습니다. 그러나이 코드는 다음과 같이 작동하지 않습니다.

class Test
{
    public static void Main()
    {
        string test = "before passing";
        Console.WriteLine(test);
        TestI(test);
        Console.WriteLine(test);
    }

    public static void TestI(string test)
    {
        test = "after passing";
    }
}

문자열을 매개 변수로 전달하고 참조 유형이므로 출력은 “전달 전” “전달 후”여야합니다. 두 번째 출력 명령문은 TestI 메소드에서 텍스트가 변경되었음을 인식해야합니다. 그러나, 나는 “통과하기 전에” “통과하기 전에”가 ref가 아닌 값으로 전달되는 것처럼 보입니다. 나는 문자열이 변경 불가능하다는 것을 알고 있지만 여기서 무슨 일이 일어나고 있는지 설명하지 못합니다. 내가 무엇을 놓치고 있습니까? 감사.



답변

문자열에 대한 참조는 값으로 전달됩니다. 값으로 참조를 전달하는 것과 참조로 객체를 전달하는 것에는 큰 차이가 있습니다. 불행히도 “참조”라는 단어는 두 경우 모두에 사용됩니다.

당신이 경우에 문자열 참조 통과 하여 참조를 예상대로, 그것은 작동합니다 :

using System;

class Test
{
    public static void Main()
    {
        string test = "before passing";
        Console.WriteLine(test);
        TestI(ref test);
        Console.WriteLine(test);
    }

    public static void TestI(ref string test)
    {
        test = "after passing";
    }
}

이제 참조가 참조하는 객체를 변경하는 것과 다른 객체를 참조 할 수 있도록 변수 (예 : 매개 변수)를 변경하는 것을 구별해야합니다. 문자열은 변경할 수 없기 때문에 문자열을 변경할 수 없지만 StringBuilder대신 문자열로 보여줄 수 있습니다 .

using System;
using System.Text;

class Test
{
    public static void Main()
    {
        StringBuilder test = new StringBuilder();
        Console.WriteLine(test);
        TestI(test);
        Console.WriteLine(test);
    }

    public static void TestI(StringBuilder test)
    {
        // Note that we're not changing the value
        // of the "test" parameter - we're changing
        // the data in the object it's referring to
        test.Append("changing");
    }
}

자세한 내용은 매개 변수 전달에 대한 내 기사 를 참조하십시오.


답변

질문에 대답해야 할 경우 : String은 참조 유형이며 참조로 동작합니다. 실제 문자열이 아닌 참조를 보유한 매개 변수를 전달합니다. 문제는 기능에 있습니다.

public static void TestI(string test)
{
    test = "after passing";
}

매개 변수 test는 문자열에 대한 참조를 보유하지만 사본입니다. 문자열을 가리키는 두 개의 변수가 있습니다. 그리고 문자열을 사용한 작업은 실제로 새 객체를 생성하기 때문에 로컬 복사본이 새 문자열을 가리 키도록 만듭니다. 그러나 원래 test변수는 변경되지 않습니다.

변수 ref의 값을 전달하지 않고 test단지 참조를 전달 하기 때문에 함수 선언과 호출 작업 에 넣을 제안 솔루션 . 따라서 함수 내부의 변경 사항은 원래 변수를 반영합니다.

마지막에 반복하고 싶습니다 : String은 참조 유형이지만 변경할 수 없기 때문에 줄이 test = "after passing";실제로 새 객체를 만들고 변수의 사본 test이 새 문자열을 가리 키도록 변경됩니다.


답변

다른 사람들이 언급했듯이 String.NET 의 유형은 변경할 수 없으며 참조가 값으로 전달됩니다.

이 코드가 실행 되 자마자 원래 코드에서 :

test = "after passing";

다음 test더 이상 언급하지 원래 오브젝트로. String 개체를 만들고 test관리되는 힙에서 해당 개체를 참조하도록 할당 했습니다.

눈에 띄는 공식적인 생성자가 없기 때문에 많은 사람들이 여기에 걸려 넘어 진다고 생각합니다. 이 경우 String유형이 구성 방식에 대한 언어 지원이 있기 때문에 배후에서 발생합니다 .

따라서 변경 사항 testTestI(string)메소드 범위 밖에서 보이지 않는 이유가 있습니다. 값으로 참조를 전달했으며 이제 해당 값이 변경되었습니다! 그러나 String참조가 참조로 전달 된 경우 참조가 변경되면 TestI(string)메소드 의 범위를 벗어난 참조를 보게됩니다 .

어느 심판 또는 밖으로 키워드는이 경우에 필요하다. out키워드가이 특정 상황에 약간 더 적합 하다고 생각합니다 .

class Program
{
    static void Main(string[] args)
    {
        string test = "before passing";
        Console.WriteLine(test);
        TestI(out test);
        Console.WriteLine(test);
        Console.ReadLine();
    }

    public static void TestI(out string test)
    {
        test = "after passing";
    }
}


답변

실제로 그것은 그 문제에 대한 모든 객체에 대해 동일했을 것입니다. 즉 참조 유형이며 참조로 전달하는 것은 C #에서 두 가지입니다.

이것은 작동하지만 유형에 관계없이 적용됩니다.

public static void TestI(ref string test)

또한 문자열이 참조 유형이며 특수 유형이기도합니다. 변경 불가능하도록 설계되었으므로 모든 메소드가 인스턴스를 수정하지 않습니다 (새 인스턴스를 리턴 함). 또한 성능을 위해 몇 가지 추가 사항이 있습니다.


답변

다음은 값 유형, 값별 전달, 참조 유형 및 참조 별 전달의 차이점을 고려하는 좋은 방법입니다.

변수는 컨테이너입니다.

값 유형 변수에는 인스턴스가 포함됩니다. 참조 형 변수는 다른 곳에 저장된 인스턴스에 대한 포인터를 포함합니다.

값 유형 변수를 수정하면 포함 된 인스턴스가 변경됩니다. 참조 유형 변수를 수정하면 그것이 가리키는 인스턴스가 변경됩니다.

별도의 참조 유형 변수는 동일한 인스턴스를 가리킬 수 있습니다. 따라서 동일한 인스턴스는이를 가리키는 모든 변수를 통해 변경 될 수 있습니다.

값으로 전달되는 인수는 컨텐츠의 새 사본이있는 새 컨테이너입니다. 참조에 의해 전달 된 인수는 원래 내용이있는 원래 컨테이너입니다.

값 유형 인수가 값별로 전달되는 경우 : 컨테이너가 고유하기 때문에 인수 내용을 다시 할당해도 범위를 벗어나지 않습니다. 인스턴스가 독립 사본이므로 인수를 수정해도 범위를 벗어나지 않습니다.

참조 유형 인수가 값으로 전달되는 경우 : 컨테이너가 고유하기 때문에 인수 내용을 다시 할당해도 범위를 벗어나지 않습니다. 복사 된 포인터가 공유 인스턴스를 가리 키기 때문에 인수의 내용을 수정하면 외부 범위에 영향을줍니다.

인수가 참조로 전달되는 경우 : 컨테이너가 공유되므로 인수의 내용을 다시 할당하면 외부 범위에 영향을줍니다. 인수의 내용을 수정하면 내용이 공유되므로 외부 범위에 영향을줍니다.

결론적으로:

문자열 변수는 참조 유형 변수입니다. 따라서 다른 곳에 저장된 인스턴스에 대한 포인터를 포함합니다. 값으로 전달되면 포인터가 복사되므로 문자열 인수를 수정하면 공유 인스턴스에 영향을 미칩니다. 그러나 문자열 인스턴스에는 변경 가능한 속성이 없으므로 문자열 인수는 어쨌든 수정할 수 없습니다. 참조로 전달되면 포인터의 컨테이너가 공유되므로 재 할당은 여전히 ​​외부 범위에 영향을 미칩니다.


답변

그림은 천 단어의 가치가있다 “.

여기 간단한 예가 있습니다. 귀하의 경우와 비슷합니다.

string s1 = "abc";
string s2 = s1;
s1 = "def";
Console.WriteLine(s2);
// Output: abc

이것이 일어난 일입니다.

여기에 이미지 설명을 입력하십시오

  • 1 호선과 2 : s1s2변수는 동일하게 참조하는 "abc"문자열 객체입니다.
  • 행 3 : 때문에 문자열은 불변 이므로 "abc"문자열 객체는 (자체를 수정하지 않는 "def"),하지만 새로운 "def"다음 문자열 객체 대신 작성하고, s1그것을 참조.
  • 4 행 : s2여전히 "abc"문자열 오브젝트에 대한 참조 이므로 출력입니다.

답변

위의 답변이 도움이되었으므로 매개 변수가 참조 유형 인 경우에도 ref 키워드없이 매개 변수를 전달할 때 발생하는 상황을 명확하게 보여주는 예제를 추가하고 싶습니다.

MyClass c = new MyClass(); c.MyProperty = "foo";

CNull(c); // only a copy of the reference is sent 
Console.WriteLine(c.MyProperty); // still foo, we only made the copy null
CPropertyChange(c);
Console.WriteLine(c.MyProperty); // bar


private void CNull(MyClass c2)
        {
            c2 = null;
        }
private void CPropertyChange(MyClass c2)
        {
            c2.MyProperty = "bar"; // c2 is a copy, but it refers to the same object that c does (on heap) and modified property would appear on c.MyProperty as well.
        }