参数在方法种具有按“值(by value)”和“引用(by ref)”两种传递方式,这是每个.NET程序员深入骨髓得基本概念。但是我若告诉你,.NET规定的参数传递形式其实是三种,会不会颠覆你的认知。
一、官方描述
二、TypedReference结构体
三、三个特殊的方法
四、三种参数传递方式
三种参数传递方式并非我们杜撰出来的,而是写在.NET最核心的规范文档ECMA-355中(I.12.4.1.5),原文如下:
The CLI supports three kinds of parameter passing, all indicated in metadata as part of the signature of the method. Each parameter to a method has its own passing convention (e.g., the first parameter can be passed by-value while all others are passed byref). Parameters shall be passed in one of the following ways (see detailed descriptions below):
- By-value – where the value of an object is passed from the caller to the callee.
- By-reference – where the address of the data is passed from the caller to the callee, and the type of the parameter is therefore a managed or unmanaged pointer.
- Typed reference – where a runtime representation of the data type is passed along with the address of the data, and the type of the parameter is therefore one specially supplied for this purpose.
It is the responsibility of the CIL generator to follow these conventions. Verification checks that the types of parameters match the types of values passed, but is otherwise unaware of the details of the calling convention.
三种参数传递方式如下:
基于Typed reference的传递时通过如果这个TypedReference结构体实现的,从其定义可以看出它通过字段_value保持值得引用,并利用_type确定其类型。它定义了一系列静态方法完成一些基于TypedReference得基本操作,比如创建一个TypedReference对象,将一个TypedReference对象转换成Object,获取TypedReference对象得目标类型等;
public struct TypedReference { private readonly ref byte _value; private readonly IntPtr _type; public unsafe static object ToObject(TypedReference value); public unsafe static TypedReference MakeTypedReference(object target, FieldInfo[] flds); public static Type GetTargetType(TypedReference value); public static RuntimeTypeHandle TargetTypeToken(TypedReference value); public static void SetTypedReference(TypedReference target, object value); }
TypedReference还涉及三个如下三个特殊方法或者函数,可能很多开源人员都没有见过:
我们通过如下这个简单的例子来演示上述的三种参数传递方式,它们分别体现在三个对应的方法上。模拟按照Typed reference进行参数传递的PassByTypedReference方法将参数类型定义为TypedReference,它通过断言检验传递参数的类型(通过调用__reftype方法获取),并通过调用__refvalue修改参数的值。
PassByValue(value); Debug.Assert(value == int.MinValue); PassByReference(ref value); Debug.Assert(value == int.MaxValue); value = int.MinValue; PassByTypedReference(__makeref(value)); Debug.Assert(value == int.MaxValue); static void PassByValue(int v) => v = int.MaxValue; static void PassByReference(ref int v) => v = int.MaxValue; static void PassByTypedReference(TypedReference v) { Debug.Assert(__reftype(v) == typeof(int)); __refvalue(v, int) = int.MaxValue; }