在 C# 中,我应该按值传递参数并返回相同的变量,还是按引用传递?

Posted

技术标签:

【中文标题】在 C# 中,我应该按值传递参数并返回相同的变量,还是按引用传递?【英文标题】:In C# should I pass a parameter by value and return the same variable, or pass by reference? 【发布时间】:2015-01-31 02:39:11 【问题描述】:

在一个 C# 程序中,我创建了一个从列表中删除对象的方法。用户输入要删除的项目的索引,然后要求用户确认删除,如果用户确认,则从列表中删除该项目,否则列表保持不变。 我不确定将参数传递给方法的最佳方式。我尝试通过引用传递列表(作为out 参数):

static void DeleteCustomer(out List<Customer> customers)

    // ...display list of objects for user to choose from...
    int deleteId = ReadInt("Enter ID of customer to delete: ");
    Console.Write("Are you sure you want to delete this customer?");
    if (Console.ReadLine().ToLower() == "y")
    
        customers.RemoveAt(deleteId);
    

上面的代码不起作用,因为我收到错误使用未分配的局部变量'customers'必须在控制离开当前方法之前分配输出参数'customers' 。我在想我可以按值传递列表并返回相同的列表,如下所示:

static List<Customer> DeleteCustomer(List<Customer> customers)

    int deleteId = ReadInt("Enter ID of customer to delete: ");
    Console.Write("Are you sure you want to delete this customer?");
    if (Console.ReadLine().ToLower() == "y")
    
        customers.RemoveAt(deleteId);
    
    return customers;


// ...which would be called from another method with:
List<Customer> customers = DeleteCustomer(customers);

但这似乎效率不高,因为同一个变量是按值传递然后返回的。

在这种情况下传递参数最有效的方法是什么?

【问题讨论】:

List 自动通过引用传递,因此无需返回它。调用方法将看到更改反映在其对传递的 List 的引用中。 见:Parameter passing in C# - By Jon Skeet。也试试第二种方法,不返回列表,你会看到它已经改变了。那是因为引用类型的地址是按值传递的。 但重要的是现在你不应该担心效率,考虑编写更清楚地传达意图的代码,然后再寻找改进性能。 第二种方法的返回类型可能是void @user469104 哦,对了,我没意识到,谢谢:) @user469104 它传递了一个引用,这与通过引用传递的引用并不完全一样。我明白你在说什么。 【参考方案1】:

在 C# 中,参数是按值传递的。这意味着当您将参数传递给方法时,会传递参数的副本。 C# 具有按值(如 int)和按引用(如任何类)的类型。 C# 包含一个堆栈(当推送所有变量时)和一个堆。值类型的值直接压入栈中,引用类型的引用压入栈中,被引用的值压入堆中。 当您传递引用类型(如列表)时,它会复制引用,但此副本指向列表中的同一个对象。因此,任何更改都会直接影响对象,除非您更改引用(使用 assigmet),但这不是您的情况。

这可以通过您的代码:

    static void DeleteCustomer<T>(List<T> customers)
    
        Console.WriteLine("Enter ID of customer to delete: ");
        int deleteId;
        if (int.TryParse(Console.ReadLine(), out deleteId)) // if the input is an int
        
            Console.Write("Are you sure you want to delete this customer?");
            if (Console.ReadLine().ToLower() == "y")
            
                customers.RemoveAt(deleteId);
            
        
        else
        
            Console.WriteLine("This is not valid Id");
        
    

如果您想了解 ref 和 out 关键字,我也可以为您提供帮助,但对于此示例来说不是必需的。

【讨论】:

【参考方案2】:

List 像所有引用类型一样,作为引用传递给对象,而不是它的副本。

请注意,这与说它是通过 引用传递有很大不同,因为这意味着参数的分配会传播给调用者,而它不会传递给调用者

这确实意味着对对象的修改(例如由RemoveAt 执行的修改)将自动传播给调用者。

因此,通过它;不需要返回值或out/ref 参数。

您很少将out/ref 用于引用类型,并且当用于值类型时,与返回相比,性能差异将非常小,除非您已经分析并制作了确定问题发生在那里。使用最通俗易懂的方式。

【讨论】:

List like all reference types, is passed as a reference to the object, and not a copy of it. This means that modifications will automatically propagate to the caller。这是不正确的。您不能将 null 或新实例分配给您传递的对象。引用类型的地址是按值传递的。 @Habib 完全同意。它被作为引用传递;不是通过引用。通过引用传递引用是不同的。你有一个对你更有意义的措辞吗?有时我真的希望我们有指针,它更容易解释......我还编辑(当你打字的时候?)澄清传播声明。 这是一个棘手的声明,我更倾向于说除非使用 ref/out 关键字,否则 C# 中的任何内容都不会通过引用传递。对于引用类型,引用/地址作为值传递。 @Habib 感谢您的反馈,我添加了一个说明,并且正在考虑一种方法来包含您的措辞。 @hometoast,是的。你会看到变化。调用函数将有一个新列表。

以上是关于在 C# 中,我应该按值传递参数并返回相同的变量,还是按引用传递?的主要内容,如果未能解决你的问题,请参考以下文章

Python按值传递参数和按引用传递参数

如何按值传递sql函数参数

如何在 C# 字典中按值索引过滤项目?

按值传递结果

ECMAScript 中所以函数的参数都是按值传递

C# vs Python 参数传递