在 C# 中通过引用或值传递对象

发布于 2024-12-23 15:32:54 字数 262 浏览 6 评论 0原文

在C#中,我一直认为非原始变量是通过引用传递的,而原始值是通过值传递的。

因此,当将任何非原始对象传递给方法时,对该方法中的对象所做的任何操作都会影响正在传递的对象。 (C# 101 的东西)

但是,我注意到当我传递 System.Drawing.Image 对象时,情况似乎并非如此?如果我将 System.Drawing.Image 对象传递给另一个方法,并将图像加载到该对象上,然后让该方法超出范围并返回到调用方法,则不会加载该图像在原始对象上?

这是为什么呢?

In C#, I have always thought that non-primitive variables were passed by reference and primitive values passed by value.

So when passing to a method any non-primitive object, anything done to the object in the method would effect the object being passed. (C# 101 stuff)

However, I have noticed that when I pass a System.Drawing.Image object, that this does not seem to be the case? If I pass a System.Drawing.Image object to another method, and load an image onto that object, then let that method go out of scope and go back to the calling method, that image is not loaded on the original object?

Why is this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

悸初 2024-12-30 15:32:55

对象根本不被传递。默认情况下,将对参数进行求值,并按值传递其,作为您调用的方法的参数的初始值。现在重要的一点是该值是引用类型的引用 - 一种获取对象(或 null)的方法。调用者可以看到对该对象的更改。但是,当您使用按值传递时,更改参数值以引用不同的对象将可见,这是所有类型的默认值。

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

所以:

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(...);
}

我有一篇文章对此进行了更多详细介绍。基本上,“通过引用传递”并不意味着您认为的意思。

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.

If you want to use pass-by-reference, you must use out or ref, whether the parameter type is a value type or a reference type. In that case, effectively the variable itself is passed by reference, so the parameter uses the same storage location as the argument - and changes to the parameter itself are seen by the caller.

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(...);
}

I have an article which goes into a lot more detail in this. Basically, "pass by reference" doesn't mean what you think it means.

笙痞 2024-12-30 15:32:55

添加了很多好的答案。我仍然想做出贡献,也许它会稍微澄清一些。

当您将实例作为参数传递给方法时,它会传递该实例的副本。现在,如果您传递的实例是值类型(驻留在堆栈中),您将传递该值的副本,因此如果您修改它不会反映在调用者中。如果实例是引用类型,则将引用的副本(同样位于堆栈中)传递给对象。所以你得到了对同一个对象的两个引用。他们都可以修改对象。但是,如果在方法体内实例化新对象,则引用的副本将不再引用原始对象,它将引用您刚刚创建的新对象。所以你最终会有 2 个引用和 2 个对象。

Lots of good answers had been added. I still want to contribute, might be it will clarify slightly more.

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.

奢华的一滴泪 2024-12-30 15:32:55

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

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:测试

TestObjRef:TestObjRef

One more code sample to showcase this:

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";
}

And the output:

TestPlain:0

TestRef:5

TestObjPlain:test

TestObjRef:TestObjRef

你好,陌生人 2024-12-30 15:32:55

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

UpdateImplicitly

名字:Favio,姓氏:Becerra

UpdateExplicitly< /strong>

名字:法维奥,姓氏:贝塞拉

I guess its clearer when you do it like this. I recommend downloading LinqPad to test things like this.

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";
}

And that should output

WontUpdate

First name: Egli, Last name: Becerra

UpdateImplicitly

First name: Favio, Last name: Becerra

UpdateExplicitly

First name: Favio, Last name: Becerra

狼性发作 2024-12-30 15:32:55

当您将 System.Drawing.Image 类型对象传递给方法时,您实际上是在传递对该对象的引用的副本。

因此,如果在该方法中您要加载新图像,则您将使用新的/复制的引用加载。您没有对原始内容进行更改。

YourMethod(System.Drawing.Image image)
{
    //now this image is a new reference
    //if you load a new image 
    image = new Image()..
    //you are not changing the original reference you are just changing the copy of original reference
}

When you pass the the System.Drawing.Image type object to a method you are actually passing a copy of reference to that object.

So if inside that method you are loading a new image you are loading using new/copied reference. You are not making change in original.

YourMethod(System.Drawing.Image image)
{
    //now this image is a new reference
    //if you load a new image 
    image = new Image()..
    //you are not changing the original reference you are just changing the copy of original reference
}
剩余の解释 2024-12-30 15:32:55

你是如何将对象传递给方法的?

您是否在对象的该方法中执行 new 操作?如果是这样,您必须在方法中使用ref

以下链接可以给您更好的想法。

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

How did you pass object to method?

Are you doing new inside that method for object? If so, you have to use ref in method.

Following link give you better idea.

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

青萝楚歌 2024-12-30 15:32:55
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";
  }
}
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";
  }
}
躲猫猫 2024-12-30 15:32:55

通过引用传递您只需在函数参数中添加“ref”,然后添加一个
您应该将函数声明为“静态”,因为 main 是 static(#public void main(String[] args))!

namespace preparation
{
  public  class Program
    {
      public static void swap(ref int lhs,ref int rhs)
      {
          int temp = lhs;
          lhs = rhs;
          rhs = temp;
      }
          static void Main(string[] args)
        {
            int a = 10;
            int b = 80;

  Console.WriteLine("a is before sort " + a);
            Console.WriteLine("b is before sort " + b);
            swap(ref a, ref b);
            Console.WriteLine("");
            Console.WriteLine("a is after sort " + a);
            Console.WriteLine("b is after sort " + b);  
        }
    }
}

In Pass By Reference You only add "ref" in the function parameters and one
more thing you should be declaring function "static" because of main is static(#public void main(String[] args))!

namespace preparation
{
  public  class Program
    {
      public static void swap(ref int lhs,ref int rhs)
      {
          int temp = lhs;
          lhs = rhs;
          rhs = temp;
      }
          static void Main(string[] args)
        {
            int a = 10;
            int b = 80;

  Console.WriteLine("a is before sort " + a);
            Console.WriteLine("b is before sort " + b);
            swap(ref a, ref b);
            Console.WriteLine("");
            Console.WriteLine("a is after sort " + a);
            Console.WriteLine("b is after sort " + b);  
        }
    }
}
泪之魂 2024-12-30 15:32:55

在最新版本的 C#(撰写本文时为 C# 9)中,对象默认通过 ref 传递。因此,对调用函数中的对象所做的任何更改都将保留在被调用函数中的对象中。

In the latest version of C#, which is C# 9 at this time of writing, objects are by default passed by ref. So any changes made to the object in the calling function will persist in the object in the called function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文