在C#中通过引用或值传递对象
在C#中,我一直认为非原始变量是通过引用传递的,原始值是通过值传递的。
所以当传递给一个方法的任何非原始对象时,对该方法中的对象做的任何事情都会影响被传递的对象。 (C#101的东西)
但是,我注意到,当我传递一个System.Drawing.Image对象,这似乎并不是这样的情况? 如果我将system.drawing.image对象传递给另一个方法,并将图像加载到该对象上,那么让该方法超出范围并返回调用方法,该图像不会加载到原始对象上?
为什么是这样?
对象根本不通过。 默认情况下,参数被评估,并且它的值被作为你正在调用的方法的参数的初始值按值传递。 现在重要的一点是,该值是引用类型的参考 – 一种获取对象(或null)的方法。 该对象的更改将从调用者可见。 但是,如果使用按值传递(这是所有类型的默认值),则更改参数的值以引用不同的对象将不可见。
如果要使用传递ref
,则无论参数类型是值类型还是引用类型,都必须使用out
或ref
。 在这种情况下,变量本身实际上是通过引用传递的,所以参数使用与参数相同的存储位置 – 调用者可以看到参数本身的变化。
所以:
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 TestRef(ref int i) { i = 5; } public static void TestPlain(int i) { i = 5; } public static void TestObjRef(ref string s) { s = "TestObjRef"; } public static void TestObjPlain(string s) { s = "TestObjPlain"; }
而输出:
TestPlain:0
TestRef:5
TestObjPlain:测试
TestObjRefTestObjRef
你是如何将对象传递给方法的?
你是否在做对象的新方法,如果你必须在方法中使用ref。
以下链接给你更好的主意。
http://dotnetstep.blogspot.com/2008/09/passing-reference-type-byval-or-byref.html http://msdn.microsoft.com/en-us/library/0f66670z(vs.71)。; ASPX#vclrfpassingmethodparameters_referencetypes
当您将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 }
当你这样做的时候,我想它更清楚。 我建议下载LinkPad来测试这样的事情。
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
名字:埃格利,姓:贝塞拉
UpdateImplicitly
名:Favio,姓:Becerra
UpdateExplicitly
名:Favio,姓:Becerra
接受的答案听起来有点错误和混乱。 什么是“参考副本”?
下列陈述如何有意义?
“但是,当您使用按值传递(这是所有类型的默认值)时,更改参数的值以引用不同的对象将不可见。” 按值传递不是所有类型的默认值。
他的链接中的示例尝试将对象的实例设置为null。 由于自动垃圾收集,该对象未成功设置为空。 这种方式不能被删除。
这是一篇比较Java和C#的微软文章。
从https://msdn.microsoft.com/en-us/library/ms836794.aspx
“所有对象都是引用
引用类型与C ++中的指针非常相似,特别是在将标识符设置为新的类实例时。 但是当访问这个引用类型的属性或方法时,使用“。” 运算符,这类似于访问在堆栈上创建的C ++数据实例。 所有的类实例都是使用new运算符在堆上创建的,但是不允许删除,因为两种语言都使用自己的垃圾回收机制,下面讨论。
在通过引用传递你只在函数参数中添加“ref”,并且由于main是静态的( public void main(String[] args)
),所以你应该声明函数“static”
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); } } }