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

jri*_*zzo 26 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是AssignmentTest()失败,另外两个测试方法通过.我假设问题是为testClass参数分配新实例会破坏参数引用,但不知何故显式添加ref关键字会修复此问题.

谁能对这里发生的事情做出详细的解释? 我主要是想扩展我对C#的了解; 我没有任何具体的情况我试图解决......

Ada*_*rth 33

几乎总是被遗忘的事情是,类不是通过引用传递的,对类的引用是通过值传递的.

这个很重要.不是复制整个类(在定型意义上传递值),而是复制对该类的引用(我试图避免说"指针").这是4或8个字节; 比复制整个班级更加可口,实际上意味着班级是"通过引用"传递的.

此时,该方法拥有自己对类的引用的副本.分配该参考方法(该方法重新分配仅其自己的基准副本)内作用域.

取消引用该引用(如同与类成员交谈)将按预期工作:除非您更改它以查看新实例(这是您在失败测试中执行的操作),否则您将看到基础类.

使用ref关键字有效地通过引用传递引用本身(指针指向事物的指针).

和往常一样,Jon Skeet提供了一个非常好的概述:

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

注意"参考参数"部分:

引用参数不传递函数成员调用中使用的变量的值 - 它们使用变量本身.

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


Jar*_*Par 7

C#中参数的默认约定是按值传递.无论参数是a class还是a,都是如此struct.在这种class情况下,只是通过值传递引用,而在这种struct情况下传递整个对象的浅拷贝.

当你进入TestAssignmentMethod有2只引用一个对象:testObj它生活在AssignmentTesttestClass它生活在TestAssignmentMethod.如果你是通过变异的实际对象testClasstestObj将是可见的两个引用,因为它们都指向同一个对象.在第一行虽然你执行

testClass = new TestRefClass() { TestInt = 1 }
Run Code Online (Sandbox Code Playgroud)

这会创建一个新对象并指向testClass它.这不会testObj以任何方式改变参考点的位置,因为它testClass是一个独立的副本.现在有2个对象和2个引用,每个引用指向不同的对象实例.

如果要通过引用语义进行传递,则需要使用ref参数.

  • 很好,详细的回答,但由于某种原因,当有人说"参数总是按值传递,即使它是你传递的参考"时,我总觉得很烦人.我看不出这种迂腐的区别是如何阐明的. (2认同)

小智 5

我的2分钱

当一个类被传递给一个方法时,它的内存空间地址的副本被发送(发送到你家的方向)。因此对该地址的任何操作都会影响房屋,但不会改变地址本身。(这是默认值)。

通过引用传递类(对象)具有传递其实际地址而不是地址副本的效果。这意味着如果您将一个新对象分配给通过引用传递的参数,它将更改实际地址(类似于重定位)。:D

我是这样看的。