在c#中,我一直认为非基本变量是通过引用传递的,而基本值是通过值传递的。

因此,当向一个方法传递任何非基本对象时,在方法中对该对象所做的任何操作都会影响正在传递的对象。(c# 101的东西)

然而,我已经注意到,当我传递一个System.Drawing.Image对象,这似乎不是这样的情况?如果我将system。drawing。image对象传递给另一个方法,并将图像加载到该对象上,然后让该方法超出作用域并返回调用方法,图像没有加载到原始对象上?

为什么会这样?


当前回答

我想当你这样做的时候会更清楚。我建议下载LinqPad来测试这样的事情。

void Main()
{
    var Person = new Person(){FirstName = "Egli", LastName = "Becerra"};

    //Will update egli
    WontUpdate(Person);
    Console.WriteLine("WontUpdate");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateImplicitly(Person);
    Console.WriteLine("UpdateImplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateExplicitly(ref Person);
    Console.WriteLine("UpdateExplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");
}

//Class to test
public class Person{
    public string FirstName {get; set;}
    public string LastName {get; set;}

    public string printName(){
        return $"First name: {FirstName} Last name:{LastName}";
    }
}

public static void WontUpdate(Person p)
{
    //New instance does jack...
    var newP = new Person(){FirstName = p.FirstName, LastName = p.LastName};
    newP.FirstName = "Favio";
    newP.LastName = "Becerra";
}

public static void UpdateImplicitly(Person p)
{
    //Passing by reference implicitly
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

public static void UpdateExplicitly(ref Person p)
{
    //Again passing by reference explicitly (reduntant)
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

然后输出

WontUpdate

名:Egli,姓:Becerra

更新隐式

名:法维奥,姓:贝塞拉

UpdateExplicitly

名:法维奥,姓:贝塞拉

其他回答

Objects aren't passed at all. By default, the argument is evaluated and its value is passed, by value, as the initial value of the parameter of the method you're calling. Now the important point is that the value is a reference for reference types - a way of getting to an object (or null). Changes to that object will be visible from the caller. However, changing the value of the parameter to refer to a different object will not be visible when you're using pass by value, which is the default for all types.

如果要使用引用传递,则必须使用out或ref,无论参数类型是值类型还是引用类型。在这种情况下,变量本身实际上是通过引用传递的,因此形参使用与实参相同的存储位置——调用者可以看到对形参本身的更改。

So:

public void Foo(Image image)
{
    // This change won't be seen by the caller: it's changing the value
    // of the parameter.
    image = Image.FromStream(...);
}

public void Foo(ref Image image)
{
    // This change *will* be seen by the caller: it's changing the value
    // of the parameter, but we're using pass by reference
    image = Image.FromStream(...);
}

public void Foo(Image image)
{
    // This change *will* be seen by the caller: it's changing the data
    // within the object that the parameter value refers to.
    image.RotateFlip(...);
}

我有一篇文章对此进行了更详细的介绍。基本上,“通过引用传递”并不是你所想的那样。

还有一个代码示例可以展示这一点:

void Main()
{


    int k = 0;
    TestPlain(k);
    Console.WriteLine("TestPlain:" + k);

    TestRef(ref k);
    Console.WriteLine("TestRef:" + k);

    string t = "test";

    TestObjPlain(t);
    Console.WriteLine("TestObjPlain:" +t);

    TestObjRef(ref t);
    Console.WriteLine("TestObjRef:" + t);
}

public static void TestPlain(int i)
{
    i = 5;
}

public static void TestRef(ref int i)
{
    i = 5;
}

public static void TestObjPlain(string s)
{
    s = "TestObjPlain";
}

public static void TestObjRef(ref string s)
{
    s = "TestObjRef";
}

输出:

测试平原:0 测试参考:5 TestObjPlain:test TestObjRef:TestObjRef

在c#的最新版本,即编写本文时的c# 9中,对象默认通过ref传递。因此,对调用函数中的对象所做的任何更改都将持久化在被调用函数中的对象中。

我想当你这样做的时候会更清楚。我建议下载LinqPad来测试这样的事情。

void Main()
{
    var Person = new Person(){FirstName = "Egli", LastName = "Becerra"};

    //Will update egli
    WontUpdate(Person);
    Console.WriteLine("WontUpdate");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateImplicitly(Person);
    Console.WriteLine("UpdateImplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateExplicitly(ref Person);
    Console.WriteLine("UpdateExplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");
}

//Class to test
public class Person{
    public string FirstName {get; set;}
    public string LastName {get; set;}

    public string printName(){
        return $"First name: {FirstName} Last name:{LastName}";
    }
}

public static void WontUpdate(Person p)
{
    //New instance does jack...
    var newP = new Person(){FirstName = p.FirstName, LastName = p.LastName};
    newP.FirstName = "Favio";
    newP.LastName = "Becerra";
}

public static void UpdateImplicitly(Person p)
{
    //Passing by reference implicitly
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

public static void UpdateExplicitly(ref Person p)
{
    //Again passing by reference explicitly (reduntant)
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

然后输出

WontUpdate

名:Egli,姓:Becerra

更新隐式

名:法维奥,姓:贝塞拉

UpdateExplicitly

名:法维奥,姓:贝塞拉

如何将对象传递给方法?

你在那个for object方法中做了新的吗?如果是,你必须使用ref in method。

下面的链接给你更好的想法。

http://dotnetstep.blogspot.com/2008/09/passing-reference-type-byval-or-byref.html