Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#字符串引用类型?_C#_String_Reference_Types - Fatal编程技术网

C#字符串引用类型?

C#字符串引用类型?,c#,string,reference,types,C#,String,Reference,Types,我知道C#中的“string”是一种引用类型。这在MSDN上。但是,此代码无法正常工作: class Test { public static void Main() { string test = "before passing"; Console.WriteLine(test); TestI(test); Console.WriteLine(test); } public static void

我知道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";
    }
}

输出应该是“传递前”“传递后”,因为我将字符串作为参数传递,并且它是引用类型,所以第二个output语句应该识别TestI方法中更改的文本。然而,我得到了“before passing”“before passing”,这使得它似乎是通过value而不是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");
    }
}
有关更多详细信息,请参阅。

尝试:


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

事实上,任何对象在这方面都是一样的,即作为引用类型和通过引用传递在c#中是两件不同的事情

这将起作用,但无论类型如何,都适用:

public static void TestI(ref string test)

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

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

public static void TestI(string test)
{
    test = "after passing";
}
参数
test
包含对字符串的引用,但它是一个副本。我们有两个变量指向字符串。因为任何带有字符串的操作实际上都会创建一个新对象,所以我们将本地副本指向新字符串。但是原始的
test
变量没有改变

建议的解决方案是将
ref
放入函数声明和调用中,因为我们不会传递
test
变量的值,而只传递对它的引用。因此,函数内部的任何更改都将反映原始变量


我想在结尾重复一次:String是一个引用类型,但因为它是不可变的,所以行
test=“after passed”
实际上创建了一个新对象,变量
test
的副本被更改为指向新字符串。

我相信您的代码类似于以下内容,您不应该期望值会因为与此处不同的原因发生更改:

 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
 }

下面是一个考虑值类型、按值传递、引用类型和按引用传递之间差异的好方法:

变量是一个容器

值类型变量包含一个实例。 引用类型变量包含指向存储在其他位置的实例的指针

修改值类型变量会改变它包含的实例。 修改引用类型变量会改变它所指向的实例

单独的引用类型变量可以指向同一实例。 因此,同一实例可以通过指向它的任何变量进行变异

“按值传递”参数是包含内容新副本的新容器。 按引用传递参数是包含其原始内容的原始容器

通过值传递值类型参数时: 由于容器是唯一的,因此重新指定参数的内容在范围外没有影响。 修改参数在范围之外没有效果,因为实例是独立副本

按值传递引用类型参数时: 由于容器是唯一的,因此重新指定参数的内容在范围外没有影响。 修改参数的内容会影响外部范围,因为复制的指针指向共享实例

通过引用传递任何参数时: 重新分配参数的内容会影响外部范围,因为容器是共享的。 修改参数的内容会影响外部范围,因为内容是共享的

总之:

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

正如其他人所述,.NET中的
字符串是不可变的,它的引用是通过值传递的

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

test = "after passing";
然后,
test
不再引用原始对象。我们已经创建了一个新的
字符串
对象,并指定
测试
在托管堆上引用该对象

我觉得很多人在这里会被绊倒,因为没有可见的正式构造函数来提醒他们。在本例中,它发生在幕后,因为
String
类型在其构造方式上具有语言支持

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

refclass 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"; } }
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.
        }
unsafe
{
     string a = "Test";
     string b = a;
     fixed (char* p = a)
     {
          p[0] = 'B';
     }
     Console.WriteLine(a); // output: "Best"
     Console.WriteLine(b); // output: "Best"
}
string b = "h";  
b += "ello";  
string s1 = "abc";
string s2 = s1;
s1 = "def";
Console.WriteLine(s2);
// Output: abc
class Test
{
    public static void Main()
    {
        string[] test = new string[1] {"before passing"};
        Console.WriteLine(ref test);
        TestI(test);
        Console.WriteLine(ref test);
    }

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