C#引用传递(ref关键字)问题

3
我不太理解在C#中ref关键字的作用。在我的C++代码中,我创建了一个字符串变量以及一个指向它的指针和引用变量。我将它传递给DeleteChange函数,正如预期的那样,所有3个变量都被修改了。
然而,在C#中,我创建了一个变量x并使用字符串数据对其进行初始化。我创建了一个变量y,应该在幕后是x的引用。然后,我使用ref关键字将x传递到ChangeDelete方法中,导致方法内部的变量以及x被修改。但是,即使y应该像在C++中一样是x的引用,它也没有被修改。这不仅仅是针对字符串的问题,因为我已经尝试过使用C#对象进行上述测试,并且得到了相同的结果。
这是否意味着在执行var y = x时,y通过值复制了x
#include <iostream>
using namespace std;

void Delete(string &variable)
{
    variable = "";
}

void Change(string &variable)
{
    variable = "changed data";
}

void main()
{
    string x = "data";
    string *y = &x;
    string &z = x;
    Delete(x);
}

c#

namespace ConsoleApplication1
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var x = "data";
            var y = x;
            Change(ref x);
        }


        public static void Delete(ref string variable)
        {
            variable = null;
        }

        public static void Change(ref string variable)
        {
            variable = "changed";
        }
    }
}

@mash:在那个例子中,左侧被改变了 - 在这个例子中,右侧被改变了。 - Dave Doknjas
问题不在于'ref'关键字,而在于当x改变时,y没有更新为x的值。 - Dave Doknjas
还要考虑在C#中使用“out”关键字而不是“ref”(请参见:https://dev59.com/BXI_5IYBdhLWcg3wHvU5) - Sal
你甚至不需要使用“Change”或“Delete”方法来展示C++中的行为。只需使用“x =“foo””而不是方法调用,您就可以得到在C#中无法重现的相同场景。 - Dave Doknjas
@Paul:但这并没有解决核心问题——在C#中,当'x'改变后,'y'不会改变——在语句级别上,没有C#的等效方法。 - Dave Doknjas
3个回答

3

这行代码: var y = x; 在C#中,将y设置为x的一个副本而不是对x的引用, 因此当调用Changed函数时,y不应该改变。

似乎没有一种简单的方法可以在C#(或大多数其他语言)中重现相同的C++行为。

当您将非值类型对象作为参数传递给函数时,对象按值传递,但对象内容(属性/字段等)按引用传递(您可以从函数内部更改它们);当您使用"ref"关键字传递对象时,对象本身是按引用传递的,也可以被更改,(这类似于在C++ / C中向结构体发送指针vs发送双重指针(其中结构体本身持有指针))。


如果'y'成为'x'的一个副本,那么如果'x'是一个具有名为'Value'的字符串属性的对象,并且该对象被传递到一个没有'ref'关键字的函数中,并且该函数修改了'Value'属性。为什么之后'x'和'y'的'Value'属性都会被修改?如果'y'是按值复制的,那么它不会忽略对'x'所做的更改吗? - Sharpiro
1
@Sharpiro:没有使用'ref',对象内部状态的更改仍然是允许的 - 只有在方法调用后对象标识保持不变。换句话说,(没有'ref')对象保证仍然是同一个对象,但其状态可以被修改。 - Dave Doknjas
正确的表述应该是:对象不会被传递到方法中,而是传递对对象的引用,但这些引用本身是按值传递的。当一个方法接收到一个对象的引用时,方法可以直接操作该对象,但是引用值无法更改以引用新对象 请参见此链接 - alelom

1

除了使用“不安全”代码之外,在C#中无法复制此行为。 大多数其他语言都无法像C ++一样使指针保持固定,即使第二个变量的标识发生更改。

这不是字符串或任何其他类型的特殊功能。 例如,在以下C ++代码的末尾,“g”指向更新的“f”,该变量被设置为“SomeOtherFoo”:

Foo f;
Foo *g = &f;
f = SomeOtherFoo();
//'g' points to the updated 'f'

在“安全”的C#中,以下代码的末尾,“g”仍然是原始对象,而不是由“SomeOtherFoo”返回的新对象:
Foo f = new Foo();
Foo g = f;
f = SomeOtherFoo();
//'g' does not point to the updated 'f'

0
问题的核心在于,在C#中字符串被视为值类型(基元),而在C++中被视为引用类型。因此,将一个字符串赋给另一个变量将简单地复制该值,正如Dr.Haimovitz所写。
在C#中访问变量的实际指针的一种方法是编写“不安全的代码”。 https://msdn.microsoft.com/en-us/library/aa288474(v=vs.71).aspx

我非常确定这是错误的。我相信字符串是具有值行为的引用类型。https://dev59.com/ZnRB5IYBdhLWcg3wZmdz - Sharpiro
我修改了上面的答案,使其更加准确。 - Sal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接