C# 字符串引用类型?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1096449/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 08:07:05  来源:igfitidea点击:

C# string reference type?

c#stringreferencetypes

提问by

I know that "string" in C# is a reference type. This is on MSDN. However, this code doesn't work as it should then:

我知道 C# 中的“字符串”是引用类型。这是在 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";
    }
}

The output should be "before passing" "after passing" since I'm passing the string as a parameter and it being a reference type, the second output statement should recognize that the text changed in the TestI method. However, I get "before passing" "before passing" making it seem that it is passed by value not by ref. I understand that strings are immutable, but I don't see how that would explain what is going on here. What am I missing? Thanks.

输出应该是“传递之前”“传递之后”,因为我将字符串作为参数传递并且它是引用类型,第二个输出语句应该识别出 TestI 方法中的文本已更改。但是,我得到“在通过之前”“在通过之前”,这使得它似乎是通过值而不是 ref 传递的。我知道字符串是不可变的,但我不明白这将如何解释这里发生的事情。我错过了什么?谢谢。

回答by Jon Skeet

The reference to the string is passed by value. There's a big difference between passing a reference by value and passing an object by reference. It's unfortunate that the word "reference" is used in both cases.

对字符串的引用是按值传递的。按值传递引用和按引用传递对象之间存在很大差异。不幸的是,在这两种情况下都使用了“参考”一词。

If you dopass the string reference byreference, it will work as you expect:

如果你这样做把这个字符串参考基准,它会像您期望的工作:

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

Now you need to distinguish between making changes to the object which a reference refers to, and making a change to a variable (such as a parameter) to let it refer to a different object. We can't make changes to a string because strings are immutable, but we can demonstrate it with a StringBuilderinstead:

现在您需要区分对引用所引用的对象进行更改,以及对变量(例如参数)进行更改以使其引用不同的对象。我们不能对字符串进行更改,因为字符串是不可变的,但是我们可以用一个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");
    }
}

See my article on parameter passingfor more details.

有关更多详细信息,请参阅我关于参数传递的文章

回答by Marius Kjeldahl

Try:

尝试:


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

回答by eglasius

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

实际上对于任何对象来说都是一样的,即作为引用类型和通过引用传递在 c# 中是 2 种不同的东西。

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.

同样关于字符串是一种引用类型,它也是一个特殊的类型。它被设计为不可变的,所以它的所有方法都不会修改实例(它们返回一个新的)。它还有一些额外的东西来提高性能。

回答by Martin Dimitrov

If we have to answer the question: String is a reference type and it behaves as a reference. We pass a parameter that holds a reference to, not the actual string. The problem is in the function:

如果我们必须回答这个问题:String 是一种引用类型,它的行为就像一个引用。我们传递一个参数来保存对的引用,而不是实际的字符串。问题出在函数上:

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

The parameter testholds a reference to the string but it is a copy. We have two variables pointing to the string. And because any operations with strings actually create a new object, we make our local copy to point to the new string. But the original testvariable is not changed.

该参数test包含对字符串的引用,但它是一个副本。我们有两个变量指向字符串。并且因为任何对字符串的操作实际上都会创建一个新对象,所以我们使本地副本指向新字符串。但原始test变量没有改变。

The suggested solutions to put refin the function declaration and in the invocation work because we will not pass the value of the testvariable but will pass just a reference to it. Thus any changes inside the function will reflect the original variable.

放入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 our copy of the variable testis changed to point to the new string.

最后我想重复一遍:String 是一种引用类型,但由于它是不可变的,该行test = "after passing";实际上创建了一个新对象,并且我们的变量副本test已更改为指向新字符串。

回答by Dave Cousineau

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
 }

回答by Bryan

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.

字符串变量是引用类型变量。因此,它包含一个指向存储在别处的实例的指针。当按值传递时,它的指针被复制,因此修改字符串参数应该影响共享实例。但是,字符串实例没有可变属性,因此无论如何都不能修改字符串参数。当按引用传递时,指针的容器是共享的,因此重新分配仍然会影响外部作用域。

回答by Derek W

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

正如其他人所说,String.NET 中的类型是不可变的,它的引用是按值传递的。

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

在原始代码中,只要这一行执行:

test = "after passing";

then testis no longer referringto the original object. We've created a newStringobject and assigned testto reference that object on the managed heap.

thentest不再原始对象。我们已经创建了一个String对象并被分配test到托管堆上引用该对象。

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 Stringtype has language support in how it is constructed.

我觉得很多人在这里被绊倒了,因为没有明显的正式构造函数来提醒他们。在这种情况下,它发生在幕后,因为该String类型在其构造方式方面具有语言支持。

Hence, this is why the change to testis 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 Stringreference were passed by reference, then when the reference changed we will see it outside the scope of the TestI(string)method.

因此,这就是为什么在方法test范围之外看不到更改为的原因TestI(string)- 我们已经通过值传递了引用,现在该值已更改!但是如果String引用是通过引用传递的,那么当引用发生变化时,我们会在TestI(string)方法的范围之外看到它。

Either the refor outkeyword are needed in this case. I feel the outkeyword might be slightly better suited for this particular situation.

无论是REF需要在这种情况下关键字。我觉得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";
    }
}

回答by BornToCode

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:

上面的答案很有帮助,我只想添加一个示例,我认为它可以清楚地说明当我们传递没有 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.
        }

回答by Naser Yousefi

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

对于好奇的头脑并完成对话: 是的,String 是一种引用类型

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 an unsafeblock! because Strings are immutable(From MSDN):

但请注意,此更改仅适用于不安全的块!因为字符串是不可变的(来自 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.

字符串对象的内容在对象创建后无法更改,尽管语法使它看起来好像可以这样做。例如,当您编写此代码时,编译器实际上会创建一个新的字符串对象来保存新的字符序列,并将该新对象分配给 b。字符串“h”然后有资格进行垃圾收集。

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.

尽管字符串是引用类型,但定义了相等运算符(==!=)来比较字符串对象的值,而不是引用。

回答by Messi

"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: s1and s2variables 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 s1references to it.
  • Line 4: s2still references to "abc"string object, so that's the output.
  • 第1行和第2行:s1s2变量引用同一个"abc"字符串对象。
  • 第 3 行:因为字符串是不可变的,所以"abc"字符串对象不会修改自己(到"def"),而是"def"创建一个新的字符串对象,然后s1引用它。
  • 第 4 行:s2仍然引用"abc"字符串对象,这就是输出。