在C#中,是否有必要通过引用传递一个对象?

3

我正在复习C#的知识。之前我使用过Java,Java中的引用传递是一种非常强大的工具,我真的很想在C#中也有这个功能。但是,我需要澄清的是是否有必要通过引用传递对象,以及是否存在需要使用它的场景?StackOverflow上已经有多个关于引用的问题,它们确实很有启发性,但我想知道是否存在需要通过引用传递对象的场景。让我附上代码以更好地说明。

class Program
    {
        public static void Update(int x) { x++; }
        public static void Update(ref int x) { x++; }
        public static void Update(Employee x) { x.msalary += 2000; }
        public static void Update(ref Employee x) { x.msalary += 2000; } //Do we even need this method? Is there a scenario where we might need this?

        static void Main(string[] args)
        {

            int a = 0;

            Update(a);
            Console.WriteLine("T1  " + a.ToString()); //a is still 0

            Update(ref a);
            Console.WriteLine("T2  " + a.ToString());//a is 1

            Employee emp2 = new Employee("Brent", "1234", "Blue", 1000); //salary is 1000

            Update(emp2.msalary);
            Console.WriteLine("T3  " + emp2.msalary.ToString());//salary does not change.

            Update(ref emp2.msalary);
            Console.WriteLine("T4  "+emp2.msalary.ToString());//salary changes to 1001

            Update(emp2);
            Console.WriteLine("T5  " + emp2.msalary.ToString()); //This changes, as expected for objects.

            Update(ref emp2);
            Console.WriteLine("T6  " + emp2.msalary.ToString()); //This also changes. But is there a scenario where we might need this?


        }
    }

1
您的 Update(ref Employee x) 方法可以执行 x = new Employee(),并且这个更改会在调用者中反映出来。 - canton7
4
对于值类型和引用类型,这种情况是一样的——当你需要在被调用的函数中对变量进行赋值,并使其在调用方可见。在您的 ref Employee 示例中没有对 x 进行赋值,因此不需要这样做。 - Lee
我认为仅针对值类型,只有在您想更改该值并使其在作用域外反映出来时使用。仅针对引用类型,只有在当前实例可能为空或者需要将新实例分配给引用时才使用。 - Jonathan Alfaro
1
在您的情况下,对Employee的引用没有被修改。您只是修改了实际的对象,而不是对它的引用。 - Jonathan Alfaro
1
C# 中有哪些有用的场景与 Java 有什么不同,你认为呢? - ADyson
显示剩余3条评论
1个回答

7
在我看来,“ref”这个名称是一个错误;正确的方式是将这个特性视为使本地或参数成为另一个变量的别名。也就是说,当你使用“ref”时,你只是给现有变量起了另一个名字。
那么问题来了:在什么情况下修改他人的变量才有意义?这些情况就是你应该使用“ref”的情况。
早期,“ref”的主要用途是类似于“bool TryParse(string s, out int x)”这样的方法,其中你想要两个返回值:一个布尔值和一个整数。但是,该方法是在C# 1天的泛型、可空类型和元组之前创建的。现在更好的做法是:如果你需要返回可能无效的值类型,请返回可空类型;如果你需要返回两个值,请返回元组。(请记住,“out”只是需要在读取之前写入的“ref”。)
那么,在使用元组和可空值类型的新代码中,“ref”的当前用例是什么呢?有一些算法可以通过直接读取和修改数据结构另一部分中的变量来获得少量性能提升,但是你需要传递哪个变量需要读取和修改。也就是说,“ref”应该被用作某些数据类型实现细节的性能优化。(请记住,你不能永久存储引用;你只能使本地成为另一个变量的别名,而该本地变量的寿命不能延长!这严重限制了引用的用例。)
你还可以使用“ref”作为与使用指针作为变量别名的非托管代码进行交互的更清晰、类型安全的方式。
大概就是这样了。我几乎从不在主流的业务代码中使用“ref”。它存在的意义在于当你需要时才使用它,但你几乎永远不需要。

@baleeghr00:在我看来,C语言没有“变量共享传递”的特性,但是C++有。 - Second Person Shooter
1
指针如果不是变量的别名,那它们是什么? - Eric Lippert
在C/C++中,与指针类型的内存存储位置相关联的变量包含一个对象的地址(也称为C#中的引用)。它与C#中引用类型的变量相同。别名共享相同的内存存储位置。 - Second Person Shooter
2
当然,但我的意思是,如果我们有 int x = 123;int *p = &x;,那么 *p 也是一个变量,而且是 x 的别名。 我们不需要C ++引用;他们只是指针上愉快的糖果。 - Eric Lippert
那里的行为非常危险,你会注意到使用托管引用的 C# 中不可能编写同样的程序;如果你使用 unsafe,那么你就要对不进行这样的操作负责。 - Eric Lippert
显示剩余5条评论

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