Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
412 views
in Technique[技术] by (71.8m points)

reference - Passing a class as a ref parameter in C# does not always work as expected. Can anyone explain?

I always thought that a method parameter with a class type is passed as a reference parameter by default. Apparently that is not always the case. Consider these unit tests in C# (using 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);
    }
}

The results are that AssignmentTest() fails and the other two test methods pass. I assume the issue is that assigning a new instance to the testClass parameter breaks the parameter reference, but somehow explicitly adding the ref keyword fixes this.

Can anyone give a good, detailed explanation of whats going on here? I'm mainly just trying to expand my knowledge of C#; I don't have any specific scenario I'm trying to solve...

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The thing that is nearly always forgotten is that a class isn't passed by reference, the reference to the class is passed by value.

This is important. Instead of copying the entire class (pass by value in the stereotypical sense), the reference to that class (I'm trying to avoid saying "pointer") is copied. This is 4 or 8 bytes; much more palatable than copying the whole class and in effect means the class is passed "by reference".

At this point, the method has it's own copy of the reference to the class. Assignment to that reference is scoped within the method (the method re-assigned only its own copy of the reference).

Dereferencing that reference (as in, talking to class members) would work as you'd expect: you'd see the underlying class unless you change it to look at a new instance (which is what you do in your failing test).

Using the ref keyword is effectively passing the reference itself by reference (pointer to a pointer sort of thing).

As always, Jon Skeet has provided a very well written overview:

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

Pay attention to the "Reference parameters" part:

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

If the method assigns something to a ref reference, then the caller's copy is also affected (as you have observed) because they are looking at the same reference to an instance in memory (as opposed to each having their own copy).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...