Programming

C # 문자열 참조 유형?

procodes 2020. 6. 7. 18:13
반응형

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단지 참조를 전달 하기 때문에 함수 선언과 호출 작업 에 넣을 제안 솔루션 . 따라서 함수 내부의 변경 사항은 원래 변수를 반영합니다.

I want to repeat at the end: String is a reference type but since its immutable the line test = "after passing"; actually creates a new object and out copy of the variable test is changed to point to the new string.


As others have stated, the String type in .NET is immutable and it's reference is passed by value.

In the original code, as soon as this line executes:

test = "after passing";

then test is no longer referring to the original object. We've created a new String object and assigned test to reference that object on the managed heap.

I feel that many people get tripped up here since there's no visible formal constructor to remind them. In this case, it's happening behind the scenes since the String type has language support in how it is constructed.

Hence, this is why the change to test is not visible outside the scope of the TestI(string) method - we've passed the reference by value and now that value has changed! But if the String reference were passed by reference, then when the reference changed we will see it outside the scope of the TestI(string) method.

Either the ref or out keyword are needed in this case. I feel the out keyword might be slightly better suited for this particular situation.

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";
    }
}

Actually it would have been the same for any object for that matter i.e. being a reference type and passing by reference are 2 different things in c#.

This would work, but that applies regardless of the type:

public static void TestI(ref string test)

Also about string being a reference type, its also a special one. Its designed to be immutable, so all of its methods won't modify the instance (they return a new one). It also has some extra things in it for performance.


Here's a good way to think about the difference between value-types, passing-by-value, reference-types, and passing-by-reference:

A variable is a container.

A value-type variable contains an instance. A reference-type variable contains a pointer to an instance stored elsewhere.

Modifying a value-type variable mutates the instance that it contains. Modifying a reference-type variable mutates the instance that it points to.

Separate reference-type variables can point to the same instance. Therefore, the same instance can be mutated via any variable that points to it.

A passed-by-value argument is a new container with a new copy of the content. A passed-by-reference argument is the original container with its original content.

When a value-type argument is passed-by-value: Reassigning the argument's content has no effect outside scope, because the container is unique. Modifying the argument has no effect outside scope, because the instance is an independent copy.

When a reference-type argument is passed-by-value: Reassigning the argument's content has no effect outside scope, because the container is unique. Modifying the argument's content affects the external scope, because the copied pointer points to a shared instance.

When any argument is passed-by-reference: Reassigning the argument's content affects the external scope, because the container is shared. Modifying the argument's content affects the external scope, because the content is shared.

In conclusion:

A string variable is a reference-type variable. Therefore, it contains a pointer to an instance stored elsewhere. When passed-by-value, its pointer is copied, so modifying a string argument should affect the shared instance. However, a string instance has no mutable properties, so a string argument cannot be modified anyway. When passed-by-reference, the pointer's container is shared, so reassignment will still affect the external scope.


Above answers are helpful, I'd just like to add an example that I think is demonstrating clearly what happens when we pass parameter without the ref keyword, even when that parameter is a reference type:

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.
        }

"A picture is worth a thousand words".

I have a simple example here, it's similar to your case.

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

This is what happened:

enter image description here

  • Line 1 and 2: s1 and s2 variables reference to the same "abc" string object.
  • Line 3: Because strings are immutable, so the "abc" string object do not modify itself (to "def"), but a new "def" string object is created instead, and then s1 references to it.
  • Line 4: s2 still references to "abc" string object, so that's the output.

For curious minds and to complete the conversation: Yes, String is a reference type:

unsafe
{
     string a = "Test";
     string b = a;
     fixed (char* p = a)
     {
          p[0] = 'B';
     }
     Console.WriteLine(a); // output: "Best"
     Console.WriteLine(b); // output: "Best"
}

But note that this change only works in a unsafe block! because Strings are immutable (From MSDN):

The contents of a string object cannot be changed after the object is created, although the syntax makes it appear as if you can do this. For example, when you write this code, the compiler actually creates a new string object to hold the new sequence of characters, and that new object is assigned to b. The string "h" is then eligible for garbage collection.

string b = "h";  
b += "ello";  

And keep in mind that:

Although the string is a reference type, the equality operators (== and !=) are defined to compare the values of string objects, not references.


I believe your code is analogous to the following, and you should not have expected the value to have changed for the same reason it wouldn't here:

 public static void Main()
 {
     StringWrapper testVariable = new StringWrapper("before passing");
     Console.WriteLine(testVariable);
     TestI(testVariable);
     Console.WriteLine(testVariable);
 }

 public static void TestI(StringWrapper testParameter)
 {
     testParameter = new StringWrapper("after passing");

     // this will change the object that testParameter is pointing/referring
     // to but it doesn't change testVariable unless you use a reference
     // parameter as indicated in other answers
 }

Try:


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

참고URL : https://stackoverflow.com/questions/1096449/c-sharp-string-reference-type

반응형