c# - 在 C# 中将类作为 ref 参数传递并不总是按预期工作。谁能解释一下?

标签 c# reference

我一直以为类类型的方法参数默认是作为引用参数传递的。显然情况并非总是如此。在 C# 中考虑这些单元测试(使用 MSTest)。

[TestClass]
public class Sandbox
{
    private class TestRefClass
    {
        public int TestInt { get; set; }
    }

    private void TestDefaultMethod(TestRefClass testClass)
    {
        testClass.TestInt = 1;
    }

    private void TestAssignmentMethod(TestRefClass testClass)
    {
        testClass = new TestRefClass() { TestInt = 1 };
    }

    private void TestAssignmentRefMethod(ref TestRefClass testClass)
    {
        testClass = new TestRefClass() { TestInt = 1 };
    }

    [TestMethod]
    public void DefaultTest()
    {
        var testObj = new TestRefClass() { TestInt = 0 };
        TestDefaultMethod(testObj);
        Assert.IsTrue(testObj.TestInt == 1);
    }

    [TestMethod]
    public void AssignmentTest()
    {
        var testObj = new TestRefClass() { TestInt = 0 };
        TestAssignmentMethod(testObj);
        Assert.IsTrue(testObj.TestInt == 1);
    }

    [TestMethod]
    public void AssignmentRefTest()
    {
        var testObj = new TestRefClass() { TestInt = 0 };
        TestAssignmentRefMethod(ref testObj);
        Assert.IsTrue(testObj.TestInt == 1);
    }
}

结果是 AssignmentTest() 失败,其他两个测试方法通过。我认为问题是将新实例分配给 testClass 参数会破坏参数引用,但不知何故显式添加 ref 关键字可以修复此问题。

谁能详细解释这里发生的事情?我主要只是想扩展我对 C# 的了解;我没有要解决的任何特定场景...

最佳答案

几乎总是被遗忘的一件事是类不是按引用传递的,对类的引用是按值传递的。

这很重要。不是复制整个类(在刻板印象中按值传递),而是复制对该类的引用(我尽量避免说“指针”)。这是 4 或 8 个字节;比复制整个类更容易接受,实际上意味着该类是“通过引用”传递的。

此时,方法拥有自己的类引用副本。将引用 分配给方法内的范围(该方法仅重新分配它自己的引用副本)。

取消对该引用的引用(例如,与类成员交谈)将按您预期的方式工作:除非您将其更改为查看新实例(这就是您在失败测试中所做的),否则您会看到底层类).

使用 ref 关键字可以有效地通过引用(指向指针之类的指针)传递引用本身。

一如既往,Jon Skeet 提供了一篇写得很好的概述:

http://www.yoda.arachsys.com/csharp/parameters.html

注意“引用参数”部分:

Reference parameters don't pass the values of the variables used in the function member invocation - they use the variables themselves.

如果该方法将某些内容分配给 ref 引用,那么调用者的副本也会受到影响(正如您所观察到的),因为他们正在查看相同 引用内存中的一个实例(而不是每个实例都有自己的副本)。

关于c# - 在 C# 中将类作为 ref 参数传递并不总是按预期工作。谁能解释一下?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9996359/

相关文章:

c++ - 为什么我可以为引用分配新值,以及如何使引用引用其他内容?

dictionary - 为什么 slice 值有时会过时但永远不会映射值?

c++ - 按值、引用和名称调用

generics - 有没有一种安全的方法可以为函数的不可变和可变变体重用相同的代码?

c# - Linq SQL GroupJoin 错误

c# - 如何在通用应用程序中禁用任务并行库的 ETW 事件源?

c# - 正则表达式拆分逗号分隔的大括号,应该很简单

c# - FirstOrDefault() 方法是处理这种情况的好方法吗

c++ - 在基于范围的 for 循环中 & 和 && 有什么区别?

c# - 如何覆盖 ASP.NET MVC 3 默认模型绑定(bind)器以在模型创建期间解决依赖关系(使用 ninject)?