__arglist 关键字的用法
__arglist 关键字作为 C# 里一个比较隐蔽的知识点(实际上也确实基本上用不到),这里为各位作出一个简要的说明和使用介绍。
变长参数兼容和 params 关键字
__arglist 关键字基本用途是兼容 C/C++ 提供的边长参数。在 C/C++ 之中,变长参数我们使用的是三个小数点来呈现的。但是在 C# 里,我们没有这样的语法,因此无法在 C# 里完成兼容。不过,C# 里有一个隐蔽的关键字“__arglist”,可以用来兼容它。
可能有小伙伴会觉得,C# 有一个 params 关键字提供的变长参数,这个是不是就可以了呢?实际上并不是。C/C++ 提供的变长参数实现规范并不是 C# 这样。C# 的 params 参数在底层被直接当场改成了一维数组,所以这也是为什么 C# 里你必须用 params 修饰符去修饰一维数组参数的原因;但 C/C++ 里并非如此。C/C++ 的变长参数在底层是有一个实际的类型在做这个任务。对了,va_args。
__arglist 的用法
C# 里提供了与之兼容的类型:RuntimeArgumentHandle,可以提供等价的操作。用法是这样的:
我们直接将 __arglist 修饰符当成参数写在参数表列上,参数表列的参数名、参数类型全部被该关键字所代替。
接着,__arglist 就表示了一个变长参数序列。现在,我们需要使用它。我们在第一行执行代码里,实例化一个 ArgIterator 类型的实例,并抄写该关键字到里面去当参数。
然后,得到 args 变量后,我们可使用 for 循环对其进行迭代。注意,args 封装的类型 ArgIterator 里只有 GetRemainingCount 这样的方法来控制当前迭代的位置。正是因为这个原因,我们这里需要使用倒序迭代,即从“长度”迭代到 1,而不是从 0 到“长度 - 1”。
接着,我们在循环内部使用 TypedReference.ToObject 方法,将该 args 变量得到的当前迭代成员取出,当成参数传入。该静态方法将直接返回 object 类型的实例,即当前迭代的成员的值。
最后,我们只需要在 C# 里输出该结果即可。
调用方用法
那么,这样的方法如何被 C# 进行使用呢?其实并不是很难。我们仍需要使用 __arglist 关键字。只不过这次我们放在调用方,用法是在关键字后添加一对小括号,然后将参数写入其中即可:
这样就可以了。这样就可以调用到刚才的方法。我们来看一下完整代码。
这里稍作解释。
file 关键字是 C# 11 提供的关键字,表示该类型仅用于当前文件范围之中。它属于一种访问修饰符,所以目前 C# 到 11 为止,一共有 8 种访问修饰级别:private、protected、private protected、protected internal、internal、public、InternalVisibleToAttribute 特性以及 file。
接着,我们使用的是 C# 9 语法:隐式主方法。主方法本应该声明一大堆的部分的,但在该特性之后可允许我们直接将执行代码一句一句写进文件,它们可以不属于任何一个命名空间以及类型。C# 9 约定,隐式主方法的默认包裹类型名就叫 Program,如果你需要拓展 Program 类型往里面加别的成员,可在隐式主方法的代码执行语句之后重新加上 Program 类型声明的头部,不过需要加 partial 关键字。因为主方法此时已经被我们拿出 Program 类型的声明部分了。
兼容 C/C++ 的 __arglist 用法
__arglist 关键字怎么实现兼容 C/C++ 的语法呢?我们拿 printf 函数举例。
printf 函数位于 msvc60.dll 文件之中,我们可使用如下两种导入模式进行函数导入:
导入过程是可省略 dll 文件后缀名的,估计很多人都不知道。
导入之后就可以了。这两种方法的用法和前文一致。