关于引用类型作为参数加上ref与不加ref的区别
众所周知,引用有class,object,string,delegate,,interface。
按道理说引用类型作为参数的时候是引用传递值的。最近写程序的时候无意中发现 引用类型 作为参数的时候,加上ref 与不加ref是有区别的。
以下以class类型 作为测试:
测试代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
public class Test
{
private string _message;
public string Message
{
get
{
return _message;
}
set
{
_message = value;
}
}
}
class Program
{
static void Main(string[] args)
{
Test a = new Test();
a.Message = "初始值";
Test001(a);
Console.WriteLine("没有添加ref关键字,也没有对引用参数引用了新的一个Test类型的对象,结果为:{0}",a.Message);
Test b = new Test();
b.Message = "初始值";
TestRef(b);
Console.WriteLine("没有添加ref关键字,并重新对引用参数引用了新的一个Test类型的对象,结果为:{0}", b.Message);
Test c = new Test();
c.Message = "初始值";
TestRef(ref c);
Console.WriteLine("关键字ref关键字,并重新对引用参数引用了新的一个Test类型的对象,结果为:{0}", c.Message);
Console.Read();
}
static void TestRef(Test t)
{
t = new Test();
t.Message = "对象已赋值";
}
static void TestRef(ref Test t)
{
t = new Test();
t.Message = "对象已赋值";
}
static void Test001(Test t)
{
t.Message = "对象已赋值";
}
}
}
给出每个方法的IL
TestRef(Test t)
.method private hidebysig static void TestRef(class ConsoleApplication1.Test t) cil managed
{
// 代码大小 21 (0x15)
.maxstack 8
IL_0000: nop
IL_0001: newobj instance void ConsoleApplication1.Test::.ctor()
IL_0006: starg.s t
IL_0008: ldarg.0
IL_0009: ldstr bytearray (F9 5B 61 8C F2 5D 4B 8D 3C 50 ) // .[a..]K.<P
IL_000e: callvirt instance void ConsoleApplication1.Test::set_Message(string)
IL_0013: nop
IL_0014: ret
} // end of method Program::TestRef
TestRef(ref Test t)
.method private hidebysig static void TestRef(class ConsoleApplication1.Test& t) cil managed
{
// 代码大小 22 (0x16)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: newobj instance void ConsoleApplication1.Test::.ctor()
IL_0007: stind.ref
IL_0008: ldarg.0
IL_0009: ldind.ref
IL_000a: ldstr bytearray (F9 5B 61 8C F2 5D 4B 8D 3C 50 ) // .[a..]K.<P
IL_000f: callvirt instance void ConsoleApplication1.Test::set_Message(string)
IL_0014: nop
IL_0015: ret
} // end of method Program::TestRef
Test001(Test t)
.method private hidebysig static void Test001(class ConsoleApplication1.Test t) cil managed
{
// 代码大小 14 (0xe)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldstr bytearray (F9 5B 61 8C F2 5D 4B 8D 3C 50 ) // .[a..]K.<P
IL_0007: callvirt instance void ConsoleApplication1.Test::set_Message(string)
IL_000c: nop
IL_000d: ret
} // end of method Program::Test001
[解决办法]
当然是有区别的,加ref就是按引用传递,不加就是值传递。“引用类型作为参数的时候是引用传递值的”这个不正确,只有加了ref的才是按引用传递。
[解决办法]
class Program { static void Main(string[] args) { MyClass classA = new MyClass(); Console.WriteLine(classA.Message); MyClass classA1 = methodA(classA); Console.WriteLine(classA.Message); Console.WriteLine(classA1.Message); Console.WriteLine(classA.Equals(classA1)); Console.WriteLine("--------------------------------"); MyClass classB = new MyClass(); Console.WriteLine(classB.Message); MyClass classB1 = methodB(ref classB); Console.WriteLine(classB.Message); Console.WriteLine(classB1.Message); Console.WriteLine(classB.Equals(classB1)); Console.ReadLine(); } private static MyClass methodA(MyClass myClass) { myClass = new MyClass(); myClass.Message = "MethodA"; return myClass; } private static MyClass methodB(ref MyClass myClass) { myClass = new MyClass(); myClass.Message = "MethodB"; return myClass; } } class MyClass { internal MyClass() { this.Message = "MyClass"; } internal string Message { get; set; } }
[解决办法]
static void TestRef(ref Test t){ t.Message = "对象已赋值";}
------解决方案--------------------
这种问题在CSDN求解释,很可能是:水越搅越混
你直接去看MSDN的解释就一清二楚:
http://msdn.microsoft.com/zh-cn/library/s6938f28.aspx
[解决办法]
Test c = new Test();
c.Message = "初始值";
TestRef(c);
static void TestRef(Test t)//调用时复制c引用给t,此时t和c指向相同的地址
{
t = new Test();//t指向新的地址,与c一点关系都没有了
t.Message = "对象已赋值";//所以在这里,c.Message依然是"初始值";
}
而ref则不会有这个问题,当t=new Test()时,c也指向新的地址,参考6楼的例子,看看它们是不是同一个对象就知道了。
[解决办法]
class Program
{
static void Main(string[] args)
{
Test a = new Test();
a.Message = "初始值";
Test001(a);
Console.WriteLine("没有添加ref关键字,也没有对引用参数引用了新的一个Test类型的对象,结果为:{0}", a.Message);
Test b = new Test();
b.Message = "初始值";
TestRef(b);
Console.WriteLine(b.GetHashCode());
Console.WriteLine("没有添加ref关键字,并重新对引用参数引用了新的一个Test类型的对象,结果为:{0}", b.Message);
Test c = new Test();
c.Message = "初始值";
TestRef(ref c);
Console.WriteLine(c.GetHashCode());
Console.WriteLine("关键字ref关键字,并重新对引用参数引用了新的一个Test类型的对象,结果为:{0}", c.Message);
Console.Read();
}
static void TestRef(Test t)
{
t = new Test();
Console.WriteLine(t.GetHashCode());
t.Message = "对象已赋值";
}
static void TestRef(ref Test t)
{
t = new Test();
Console.WriteLine(t.GetHashCode());
t.Message = "对象已赋值";
}
static void Test001(Test t)
{
t.Message = "对象已赋值";
}
}
[解决办法]
namespace ConsoleApplication7{ class A { public int a; public A(int temp) { a = temp; } } class Program { static void test(A aa) { aa=new A(1000); } static void testref(ref A aa) { aa = new A(1000); } static void Main(string[] args) { A a = new A(10); test(a); testref(ref a); } }}
[解决办法]
static void Main(string[] args) {00000000 push ebp 00000001 mov ebp,esp 00000003 push edi 00000004 push esi 00000005 push ebx 00000006 sub esp,38h 00000009 mov esi,ecx 0000000b lea edi,[ebp-40h] 0000000e mov ecx,0Dh 00000013 xor eax,eax 00000015 rep stos dword ptr es:[edi] 00000017 mov ecx,esi 00000019 xor eax,eax 0000001b mov dword ptr [ebp-1Ch],eax 0000001e mov dword ptr [ebp-3Ch],ecx 00000021 cmp dword ptr ds:[009D86F0h],0 00000028 je 0000002F 0000002a call 793CE1B9 0000002f nop A a = new A(10);00000030 mov ecx,9D9328h 00000035 call FFCA0E84 0000003a mov dword ptr [ebp-44h],eax 0000003d mov ecx,dword ptr [ebp-44h] 00000040 mov edx,0Ah 00000045 call FFCBB350 0000004a mov eax,dword ptr [ebp-44h] 0000004d mov dword ptr [ebp-40h],eax test(a);00000050 mov ecx,dword ptr [ebp-40h] 00000053 call FFCBB2E8 00000058 nop testref(ref a);00000059 lea ecx,[ebp-40h] 0000005c call FFCBB2F0 00000061 nop }再看看最终的汇编吧!!
[解决办法]
static void test(A aa)
{
00000000 push ebp
00000001 mov ebp,esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp,34h
00000009 xor eax,eax
0000000b mov dword ptr [ebp-10h],eax
0000000e xor eax,eax
00000010 mov dword ptr [ebp-1Ch],eax
00000013 mov dword ptr [ebp-3Ch],ecx
00000016 cmp dword ptr ds:[009D86F0h],0
0000001d je 00000024
0000001f call 793CE0E1
00000024 nop
aa=new A(1000);
00000025 mov ecx,9D9328h
0000002a call FFCA0DAC
0000002f mov dword ptr [ebp-40h],eax
00000032 mov ecx,dword ptr [ebp-40h]
00000035 mov edx,3E8h
0000003a call FFCBB278
0000003f mov eax,dword ptr [ebp-40h]
00000042 mov dword ptr [ebp-3Ch],eax
}
00000045
[解决办法]
static void testref(ref A aa) {00000000 push ebp 00000001 mov ebp,esp 00000003 push edi 00000004 push esi 00000005 push ebx 00000006 sub esp,34h 00000009 xor eax,eax 0000000b mov dword ptr [ebp-10h],eax 0000000e xor eax,eax 00000010 mov dword ptr [ebp-1Ch],eax 00000013 mov dword ptr [ebp-3Ch],ecx 00000016 cmp dword ptr ds:[009D86F0h],0 0000001d je 00000024 0000001f call 793CE081 00000024 nop aa = new A(1000);00000025 mov ecx,9D9328h 0000002a call FFCA0D4C 0000002f mov dword ptr [ebp-40h],eax 00000032 mov ecx,dword ptr [ebp-40h] 00000035 mov edx,3E8h 0000003a call FFCBB218 0000003f mov edx,dword ptr [ebp-3Ch] 00000042 mov eax,dword ptr [ebp-40h] 00000045 call 79151428 }
[解决办法]
1.将地址推送到堆栈上。
2.从堆栈中弹出地址;获取位于此地址的对象引用。
3.将获取的引用推送到堆栈上。