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

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

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

为什么会这样?


当前回答

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

如何将对象传递给方法?

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

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

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

Employee e = new Employee();
e.Name = "Mayur";

//Passes the reference as value. Parameters passed by value(default).
e.ReferenceParameter(e);

Console.WriteLine(e.Name); // It will print "Shiv"

 class Employee {

   public string Name { get; set; }

   public void ReferenceParameter(Employee emp) {

     //Original reference value updated.
    emp.Name = "Shiv";

    // New reference created so emp object at calling method will not be updated for below changes.
    emp = new Employee();
    emp.Name = "Max";
  }
}

很多好的答案被添加了进来。我还是想做点贡献,可能会更清楚一点。

When you pass an instance as an argument to the method it passes the copy of the instance. Now, if the instance you pass is a value type(resides in the stack) you pass the copy of that value, so if you modify it, it won't be reflected in the caller. If the instance is a reference type you pass the copy of the reference(again resides in the stack) to the object. So you got two references to the same object. Both of them can modify the object. But if within the method body, you instantiate new object your copy of the reference will no longer refer to the original object, it will refer to the new object you just created. So you will end up having 2 references and 2 objects.

我想当你这样做的时候会更清楚。我建议下载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

名:法维奥,姓:贝塞拉