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

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

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

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

为什么会这样?

310685 次浏览

如何将对象传递给方法?

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

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

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

对象根本没有被传递。默认情况下,参数会被求值,它的价值会作为你调用的方法的参数的初始值被传递。现在重要的一点是,该值是引用类型的引用——一种获取对象(或null)的方式。对该对象的更改将从调用者可见。然而,当你使用pass by value(这是所有类型的默认值)时,改变形参的值以引用不同的对象将会显示

如果你想使用引用传递,无论参数类型是值类型还是引用类型,你都要使用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(...);
}

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

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

如果在这个方法中你要加载一个新图像你是在使用new/ replicated reference来加载。你没有改变原来的样子。

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
}

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

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

输出:

TestPlain: 0

TestRef: 5

TestObjPlain:测试

TestObjRef: TestObjRef

In Pass By Reference你只在函数参数中添加“ref”和一个 你应该声明函数为“static”,因为main是static(#public void main(String[] args))!< / p >

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

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

名:法维奥,姓:贝塞拉

UpdateExplicitly

名:法维奥,姓:贝塞拉

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

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

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

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