为什么c#对象的行为类似于按值传递,而一次是通过引用传递的?

时间:2013-11-14 14:31:34

标签: c# reference

我不了解将参数传递给c#中的方法的一件事。从我所看到的c#中的对象有时表现得像已经通过引用传递,并且就像它们通过值传递一样。在这段代码中,我通过引用和值一次传递给method()。这两个都按预期执行。但是当我创建Update()并按值传递一个对象时,我发现它的行为就像是在更新原始对象。

为什么我使用Update(myString input)更新原始对象,但不使用method(myString input)更新原始对象?

这是不合逻辑的!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClassPassing
{
class Program
{
    static void Main(string[] args)
    {
        myString zmienna = new myString();

        Update(zmienna);
        Console.WriteLine(zmienna.stringValue);
        Console.WriteLine(zmienna.stringValue2);
        Console.ReadLine();

        zmienna.stringValue = "This has run in main";
        zmienna.stringValue2 = "This is a help string";

        method(zmienna);
        Console.WriteLine(zmienna.stringValue);
        Console.WriteLine(zmienna.stringValue2);
        Console.ReadLine();

        method(ref zmienna);
        Console.WriteLine(zmienna.stringValue);
        Console.WriteLine(zmienna.stringValue2);

        Console.ReadLine();
    }

    static void method(myString input)
    {
        input = new myString();
    }

    static void method(ref myString input)
    {
        input = new myString();
    }

    static void Update(myString input)
    {
        input.stringValue2 = "This has run in update method";
    }
}

public class myString
{
    public string stringValue { get; set; }
    public string stringValue2 { get; set; }

    public myString() { stringValue = "This has been just constructed";   this.stringValue2 = "This has been just constructed"; }
}

}`

4 个答案:

答案 0 :(得分:4)

您必须了解您的代码:

static void method(myString input)
{
    input = new myString();
}

这里按值 传递对象的引用

static void method(ref myString input)
{
    input = new myString();
}

在此您通过引用

传递对象的引用
static void Update(myString input)
{
    input.stringValue2 = "This has run in update method";
}

这里再次传递对象的引用

现在:

  1. 按值传递对象引用时,您可以更改对象的内容,但不能更改引用本身(将其分配给另一个对象)。
  2. 通过引用传递对象引用时,您既可以更改对象的内容,也可以修改引用本身(将其指定给另一个对象)。
  3. 在C#中传递值的真实仅在简单(int,float等)类型的情况下发生,并且在struct s的情况下:

    class Program 
    {
        public struct MyStruct
        {
            public int i;
        }
    
        public class MyClass 
        {
            public int i;
        }
    
        public static void Modify(MyStruct s) 
        {
            s.i = 99;
        }
    
        public static void Modify(MyClass c) 
        {
            c.i = 99;
        }
    
        public static void Main(string[] args) 
        {
            MyStruct myStruct = new MyStruct();
            myStruct.i = 20;
            MyClass myClass = new MyClass();
            myClass.i = 20;
    
            Modify(myStruct);
            Modify(myClass);
    
            Console.WriteLine("MyStruct.i = {0}", myStruct.i);
            Console.WriteLine("MyClass.i = {0}", myClass.i);
    
            Console.ReadKey();
        }
    }
    

    结果:

    MyStruct.i = 20
    MyClass.i = 99  
    

    在这种情况下,MyStruct的值保持不变,因为它是按值传递给函数。另一方面,MyClass的实例通过引用传递 ,这就是它的值发生变化的原因。

答案 1 :(得分:4)

根本没有传递

对象

对于引用类型(类,接口等)的表达式,默认情况下按值传递引用,但如果使用ref,则通过引用传递变量。

重要的是要理解zmienna的值不是对象 - 它是一个参考。一旦你对它进行了排序,剩下的就变得简单了。它不仅适用于参数传递 - 它适用于所有内容。例如:

StringBuilder x = new StringBuilder();
StringBuilder y = x;
y.Append("Foo");
Console.WriteLine(x); // Prints Foo

这里xy的值是对同一个对象的引用 - 就像有两张纸,每张纸都有相同的街道地址。因此,如果有人通过阅读x上写的地址并将前门涂成红色来访问房屋,那么其他人通过阅读y上写的地址访问同一所房子,第二个人会看到一个红色前门也是。

有关详细信息,请参阅reference and value typesparameter passing上的文章。

答案 2 :(得分:0)

这里可能有多个问题要回答,但关于你的最后一个问题:

"Why do I update original object with Update(myString input) but do not update it with method(myString input)?"

在这里,您正在创建myString类的新实例,而不是引用作为参数传递给方法的原始实例。因此,如果您在方法中更改input.stringValue2的值,则在离开方法后将丢失该值。

static void method(myString input)
{
    input = new myString();
}

但是在这里你引用传递给它的原始实例。离开此方法后,原始myString实例将保留stringValue2的值。

static void Update(myString input)
{
    input.stringValue2 = "This has run in update method";
}

答案 3 :(得分:0)

将计算机内存想象成一组盒子,并且可以使用标签为它们命名。

myString zmienna = new myString();

在这里,您可以在其中分配一个包含myString实例的框,并且标签zmienna指向它。然后:

static void method(myString input)
{
    input = new myString();
}

在此方法中,input是另一个标签。调用方法首先使标签input指向与初始实例相同的框。但是,在方法的正文中,您可以分配另一个框,并将标签 input更改为指向该新框。第一个框没有任何操作,zmienna标签没有做任何事情。

static void method(ref myString input)
{
    input = new myString();
}

在这里,由于ref关键字,您不仅要传递第一个“内存盒”的下落,而且还要提供实际标签。因此,此方法的正文更新您的标签zmienna以指向新创建的框,其中第二个实例为myString。第一个盒子被遗忘了,因为没有标签指向它。

static void Update(myString input)
{
    input.stringValue2 = "This has run in update method";
}

在这种情况下,您传递第一个框的地址,方式与第一个方法完全相同。所以你有两个实验室:zmiennainput - 都指向同一个盒子。因此,input.stringValue2会在stringValue2指向的同一个框中访问字段zmienna

实际使用的精确术语是 reference ,而不是我在此解释中使用的 label 术语。我发现许多人发现这种方式更容易理解:)