簡體   English   中英

為什么c#對象的行為一次類似於按值傳遞,而一次按引用傳遞?

[英]Why do c# objects once behave like passed by value and once as passed by 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"; }
}

}`

您必須了解您的代碼:

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. 按引用傳遞對象引用時 ,您既可以更改對象的內容,也可以修改引用本身(將其分配給另一個對象)。

僅在簡單(int,float等)類型和struct s情況下,才在C#中按值進行實際傳遞:

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的值保持不變,因為它是通過value傳遞給函數 另一方面, MyClass的實例通過引用傳遞,這就是其值更改的原因。

根本不傳遞對象

對於引用類型的表達式(類,接口等) 引用傳遞-通過值默認,但如果你使用的變量的引用傳遞ref

重要的是要了解zmienna的值不是對象,而是參考。 排序后,其余的變得簡單。 這不僅用於傳遞參數,還用於所有參數 例如:

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

這里的xy值是對同一個對象的引用-就像有兩張紙,每張紙上都有相同的街道地址。 因此,如果某人通過閱讀x寫的地址來訪問房屋並將前門塗成紅色,那么其他人通過閱讀y寫的地址來訪問同一所房屋,第二個人也將看到一個紅色的前門。

有關更多詳細信息,請參見我的有關引用和值類型以及參數傳遞的文章。

這里可能有多個問題要回答,但是關於您的最后一個問題:

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

在這里,您正在創建myString類的新實例,而不引用作為參數傳遞給該方法的原始實例。 因此,如果您在方法內部更改input.stringValue2的值,則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";
}

將計算機內存想象成一組盒子,您可以使用標簽為它們命名。

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.stringValue2zmienna指向的同一框中訪問字段stringValue2

實際使用的精確術語是參考,而不是我在此說明中使用的標簽術語。 我以某種方式發現許多人發現以這種方式更容易理解:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM