在C#中通过引用或引用类型的值作为参数传递

时间:2017-03-20 05:41:14

标签: c# class parameters reference

我在C#中学到的参考类型是通过参数传递值,除非使用outref关键字。最近我写了一些测试代码来验证这个和一些规范并得到一些意外的结果

基本思路是:我在Foo和Bar的成员之间共享了一些变量Cls(类TestClass)和Str(一个字符串)。 Bar的某些成员是直接分配的,有些是参数,有些是带有ref关键字的参数。然后我在Foo

中更改这些变量

我希望那些按价值分配的Bar成员不会改变,而那些参考的成员将会改变。但除了字符串属性外,它们都会发生变化。

以下是代码

public class FieldsAndProperties
    {
        private Foo foo;
        private Bar bar;
        public void Test()

        {
            string Str = "Original String";
            TestClass Cls = new TestClass { Content = "Original Content" };

            // These parts are seemingly passed by reference
            this.foo = new Foo
            {
                FooStringField = Str,
                FooStringProp = Str,
                FooTestField = Cls,
                FooTestProp = Cls
            };

            // -------------------------NOTICE-------------------------------------
            // I was expecting the first parameter to be passed by value, thus 
            // what it assigns to won't change after I change Cls.
            this.bar = new Bar(Cls, ref Cls);

            // I was expecting them passed by value, yet the test tells otherwise.
            this.bar.ChangeThroughPara(Cls, Cls);
            // ----------------------END OF NOTICE---------------------------------

            // bar members that are related to Str and Cls by direct assigning (by reference).
            this.bar.BarStringField = Str;
            this.bar.BarStringProp = Str;
            this.bar.BarTestField = Cls;
            this.bar.BarTestProp = Cls;

            ///<remarks>
            /// Here is where I change the <see cref="Cls"/>, which is also 
            /// tied with <see cref="foo.FooTestField"/> and <see cref="foo.FooTestProp"/>
            ///</remarks>
            this.foo.Change();

            Console.WriteLine(this.bar.ToString());

            return;
        }
    }

    class TestClass
    {
        public string Content { get; set; }
    }

    class Foo
    {
        public string FooStringField;

        public string FooStringProp { get; set; }

        public TestClass FooTestField;

        public TestClass FooTestProp { get; set; }

        /// <summary>
        /// I suppose here <see cref="FooStringField"/>, <see cref="FooStringProp"/>
        /// <see cref="FooTestField"/>, <see cref="FooTestProp"/> be assigned, also 
        /// changing the instance referenced by <see cref="Cls"/> and <see cref="Str"/>.
        /// And this is verified by the test.
        /// </summary>
        public void Change()
        {
            this.FooStringField = "Foo String";
            this.FooStringProp = "Foo String";

            this.FooTestField.Content = "Foo Content";
            this.FooTestProp.Content = "Foo Content";
        }
    }

    class Bar
    {
        public string BarStringField;
        public string BarStringProp { get; set; }
        public TestClass BarTestField;
        public TestClass BarTestProp { get; set; }
        public TestClass BarConsField;        
        public TestClass BarConsProp { get; set; }
        public TestClass BarRefField;
        public TestClass BarRefProp { get; set; }
        public TestClass BarParaField;
        public TestClass BarParaProp { get; set; }


        // -----------------------------------NOTICE---------------------------------------
        /// <summary>
        /// Constructor test.
        /// </summary>
        /// <param name="barCons">
        /// I assumed it to be passed by value, thus when <see cref="Cls"/> changes,
        /// <see cref="BarConsField"/> and <see cref="BarConsProp"/> remains the same,
        /// however it is not the case during testing.
        /// </param>
        /// <param name="barRef">
        /// Passed by reference, expected change after <see cref="Change()"/>, and it
        /// does
        /// </param>
        public Bar(TestClass barCons, ref TestClass barRef)
        {
            this.BarConsField = barCons;
            this.BarConsProp = barCons;
            this.BarRefField = barRef;
            this.BarRefProp = barRef;
        }

        /// <summary>
        /// Expecting no change in <see cref="BarParaField"/> and <see cref="BarParaProp"/>
        /// after <see cref="Change()"/>, however test tells otherwise.
        /// </summary>
        /// <param name="field"></param>
        /// <param name="prop"></param>
        public void ChangeThroughPara(TestClass field, TestClass prop)
        {
            this.BarParaField = field;
            this.BarParaProp = prop;
        }

        //----------------------------------END OF NOTICE-------------------------------------
        public override string ToString()
        {
            return $"Field: {this.BarStringField}   Property: {this.BarStringProp} \n" +
                $"FieldTest: {this.BarTestField.Content}   PropTest: {this.BarTestProp.Content} \n" +
                $"FieldCons: {this.BarConsField.Content}   PropCons: {this.BarConsProp.Content} \n" +
                $"FieldRef: {this.BarRefField.Content}   PropRef:   {this.BarRefProp.Content} \n" +
                $"FieldPara: {this.BarParaField.Content}   PropPara:   {this.BarParaProp.Content} \n";
        }
    }

结果如下: Every member except strings in Bar changed!

以下是问题:

  1. 为什么那些作为参数传递的信息后来仍然会引用 Cls受影响的。它们不应该被价值传递吗?

  2. 我认为字符串也是引用类型,为什么它们不受影响?

0 个答案:

没有答案