我在 .NET 4.5 中编写了以下 C#:
public struct DisposableStruct : IDisposable {
private readonly int _i, _j;
public DisposableStruct(int i, int j) {
_i = i;
_j = j;
}
[MethodImpl(MethodImplOptions.NoInlining)]
void IDisposable.Dispose() {
Console.WriteLine(_i + _j);
}
}
internal class Program {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Dispose<T>(ref T obj) where T : IDisposab
obj.Dispose();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void Dispose<T>(T obj) where T : IDisposable {
obj.Dispose();
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Test() {
var ds = new DisposableStruct(1, 2);
Dispose(ref ds);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void TestNoRef() {
var ds = new DisposableStruct(1, 2);
Dispose(ds);
}
private static unsafe void Main() {
Test();
TestNoRef();
Console.WriteLine(Process.GetCurrentProcess().Id);
Console.ReadLine();
}
}
暂停后我用打印的进程ID转储内存,但发现了一些奇怪的东西。这是本机代码Test
:
0:000> !u 000007fd85c30120
Normal JIT generated code
SimpleConsole.Program.Test()
Begin 000007fd85c30120, size 40
*** WARNING: Unable to verify checksum for SimpleConsole.exe
c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 40:
>>> 000007fd`85c30120 4883ec38 sub rsp,38h
000007fd`85c30124 48c744242800000000 mov qword ptr [rsp+28h],0
000007fd`85c3012d 48c744242000000000 mov qword ptr [rsp+20h],0
000007fd`85c30136 488d442420 lea rax,[rsp+20h]
000007fd`85c3013b 488d4c2428 lea rcx,[rsp+28h]
000007fd`85c30140 c70001000000 mov dword ptr [rax],1
000007fd`85c30146 c7400402000000 mov dword ptr [rax+4],2
000007fd`85c3014d 488b442420 mov rax,qword ptr [rsp+20h]
000007fd`85c30152 488901 mov qword ptr [rcx],rax
000007fd`85c30155 e83ebfeeff call 000007fd`85b1c098 (SimpleConsole.DisposableStruct.System.IDisposable.Dispose(), mdToken: 0000000006000017)
000007fd`85c3015a 90 nop
000007fd`85c3015b 4883c438 add rsp,38h
000007fd`85c3015f c3 ret
这是方法的本机代码TestNoRef
:
0:000> !u 000007fd85c301b0
Normal JIT generated code
SimpleConsole.Program.TestNoRef()
Begin 000007fd85c301b0, size 42
c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 46:
>>> 000007fd`85c301b0 4883ec38 sub rsp,38h
000007fd`85c301b4 48c744242800000000 mov qword ptr [rsp+28h],0
000007fd`85c301bd 48c744242000000000 mov qword ptr [rsp+20h],0
000007fd`85c301c6 488d442428 lea rax,[rsp+28h]
000007fd`85c301cb c70001000000 mov dword ptr [rax],1
000007fd`85c301d1 c7400402000000 mov dword ptr [rax+4],2
c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 47:
000007fd`85c301d8 488b442428 mov rax,qword ptr [rsp+28h]
000007fd`85c301dd 4889442420 mov qword ptr [rsp+20h],rax
000007fd`85c301e2 488d4c2420 lea rcx,[rsp+20h]
000007fd`85c301e7 e894ffffff call 000007fd`85c30180 (SimpleConsole.DisposableStruct.System.IDisposable.Dispose(), mdToken: 0000000006000017)
000007fd`85c301ec 90 nop
000007fd`85c301ed 4883c438 add rsp,38h
000007fd`85c301f1 c3 ret
我们可以很容易地发现相同的入口地址Dispose
是不同的:
- 测试:
000007fd 85b1c098
- 测试编号:
000007fd 85c30180
但只有地址TestNoRef
是正确的:
0:000> !u 000007fd`85c30180
Normal JIT generated code
SimpleConsole.DisposableStruct.System.IDisposable.Dispose()
Begin 000007fd85c30180, size 19
c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 22:
>>> 000007fd`85c30180 4883ec28 sub rsp,28h
000007fd`85c30184 488bc1 mov rax,rcx
000007fd`85c30187 8b08 mov ecx,dword ptr [rax]
000007fd`85c30189 8b4004 mov eax,dword ptr [rax+4]
000007fd`85c3018c 03c8 add ecx,eax
000007fd`85c3018e e85d8dda5e call mscorlib_ni+0xce8ef0 (000007fd`e49d8ef0) (System.Console.WriteLine(Int32), mdToken: 000000000600098d)
000007fd`85c30193 90 nop
000007fd`85c30194 4883c428 add rsp,28h
000007fd`85c30198 c3 ret
但是里面的那个是什么Test
?
0:000> !u 000007fd`85b1c098
Unmanaged code
000007fd`85b1c098 e89360765f call clr+0x2130 (000007fd`e5282130)
000007fd`85b1c09d 5e pop rsi
000007fd`85b1c09e 05009048b1 add eax,0B1489000h
000007fd`85b1c0a3 85fd test ebp,edi
000007fd`85b1c0a5 07 ???
000007fd`85b1c0a6 0000 add byte ptr [rax],al
000007fd`85b1c0a8 0000 add byte ptr [rax],al
000007fd`85b1c0aa 0000 add byte ptr [rax],al
000007fd`85b1c0ac 0000 add byte ptr [rax],al
000007fd`85b1c0ae 0000 add byte ptr [rax],al
程序的结果显然是正确的,但是本机代码对Test
方法的表现如何?