Variable parameter methods in C # (VarArgs)

Source: Internet
Author: User

The first thing to be clear is that the variable-parameter method mentioned here refers to a method with a Callingconventions.varargs calling convention, rather than a method containing the params parameter. You can get the calling convention for a method by using the Methodbase.callingconvention property.

For a common example, the C-language printf approach Most people should know is that the function is to write a formatted string to the standard output stream (stdout), printf the method signature is:

int printf (const char * format, ...);

in the ... method signature , it means that this method is variable Parameters , you can pass any number of parameters as needed, and the types of parameters can vary.

The params parameter in C # has a stronger constraint, although the number of parameters can be fixed, but the type of the parameter must be the same. In fact, C # can also declare mutable parameters such as the C language, but mostly to invoke methods provided by unmanaged DLLs, rather than for managed methods . This article introduces variable-parameter methods from the declarations of P/invoke, C #, variable parameter methods, IL code, and RuntimeArgumentHandle four.

P/invoke of variable parameter method

If an unmanaged DLL provides a mutable parameter method, how do you call it in C #?

The simplest way is obviously to invoke on demand--although the provided method is mutable, I probably don't need that much freedom, just one or several fixed parameters. In this case, the signature of the method should be written directly as needed, or printf as an example:

[DllImport ("Msvcrt.dll", callingconvention = callingconvention.cdecl)]public static extern int printf (string format, string text); [DllImport ("Msvcrt.dll", callingconvention = callingconvention.cdecl)]public static extern int printf (string format, int num, int x, int y);//Call Method printf ("Hello%s!\n", "World"); Hello world!printf ("Hello%d! is%d x%d\n ", 42, 6, 7); Hello 42! is 6 x 7

It is important to note that DllImport needs to be explicitly specified so that the CallingConvention = CallingConvention.Cdecl stack can be cleaned up by the caller to support mutable parameter methods.

What if you really need a full variable-parameter approach? You can do this with some special keywords that do not give an official document, but do exist in the C # compiler. Define the printf method as follows, noting that the parameter uses a __arglist keyword and does not specify any parameter types and parameter names:

[DllImport ("Msvcrt.dll", callingconvention = callingconvention.cdecl)]public static extern int printf (string format, __ arglist);

when calling a method, you must also use __arglist() the variable parameter Enclosed:

printf ("Hello%s!\n", __arglist ("World")); Hello world!printf ("Hello%s! is%d x%c\n ", __arglist (" World ", 6, ' 7 ')); Hello world! is 6 x 7

here to distinguish __arglist and __arglist() , __arglist is used for the variable parameter method of the declaration and method body reference variable parameters, and __arglist() is used with the variable parameter method of the call. Note For the second example, the three parameters are of different types, namely, String, Integer, and character.

Variable parameter method in C #

The above mentioned that the unmanaged DLL can provide a variable parameter method, C # can also invoke such a method, then C # itself can declare such a method?

The answer is obviously that since the __arglist keyword can be used in the P/invoke method, it can obviously be used in common methods. This is only when you need to use the ArgIterator structure and the TypedReference structure to access the parameters instead of the normal parameter access methods.

Let's look at a simple example:

public static void printf (string format, __arglist) {    console.write (format);    ArgIterator args = new ArgIterator (__arglist);    while (args. Getremainingcount () > 0) {        Console.WriteLine ("{0}: {1}", Type.gettypefromhandle (args. Getnextargtype ()),             typedreference.toobject (args. Getnextarg ()));}    } printf ("Hello%s! is%d x%c\n ", __arglist (" World ", 6, ' 7 '));//Hello%s! is%d x%c//system.string:world//system.int32:6//system.char:7

In this example, the use of variable parameters has been basically shown, the following is a brief introduction.

The first is ArgIterator to construct the instance by calling the constructor new ArgIterator(__arglist) .

Then, traversing the mutable parameters, the Argiterator.getremainingcount method can return the number of arguments remaining in the mutable argument list, and automatically subtract one each time the Argiterator.getnextarg method is called to get the next parameter.

The target type of the next parameter can be obtained using the Argiterator.getnextargtype method, which does not advance the iteration to the next parameter (more like a Peek method). Note that the result is a RUNTIMETYPEHANDLE structure that requires the use of the Type.gettypefromhandle method to get the type that can be used, and not the actual type of the argument, just when __arglist() the method is called The type of the target parameter specified in the. For example:

printf ("Hello%s! is%d x%c\n ", __arglist ((ienumerable<char>)" World ", (object) 6, (icomparable<char>) ' 7 '));//Hello%s! is%d x%c//System.Collections.Generic.IEnumerable ' 1[system.char]: world//system.object:6//system.icomparable ' 1[ System.Char]: 7

The type of the resulting parameter value is TypedReference, you need to use the Typedreference.toobject static method to get the actual value of the parameter. There are two additional static methodsthat need to be noted for TypedReference: Gettargettype and Targettypetoken, which The Argiterator.getnextargtype method can only get the target parameter type when it is called.

There are some unpublished keywords about typedreference, but they are not recommended because they are generally not available, or have alternative hosting methods.

__makerefTo create a TypedReference instance:

String str = "any value"; TypedReference typeRef = __makeref (str);

__refvalue, used to get or set The value of the TypedReference instance requires that the type must be exactly the same as the target type of the TypedReference, and that it is more bizarre to use:

__refvalue (TypeRef, string) = "other value"; Console.WriteLine (__refvalue (TypeRef, String));

Note that this is still the target type, not the actual type of the value:

Object str = "any value"; TypedReference typeRef = __makeref (str); __refvalue (TypeRef, object) = "other value"; Console.WriteLine (__refvalue (TypeRef, object));

__reftype, used to obtain the target type of TypedReference, which is equivalent to Typedreference.gettargettype :

Console.WriteLine (__reftype (TYPEREF));

Once again, __arglist Other keywords are not recommended except for keywords . The syntax checker for Visual Studio 2013 identifies __arglist keywords, and other keywords prompt for syntax errors (but can be compiled through).

The variable parameter methods in C # have the following characteristics:

    • Variadic methods are not CLS-compliant.
    • An interface can declare a mutable parameter method, which can also be a virtual method, and can be overridden by a subclass.
    • The number of arguments obtained by reflection will only contain fixed parameters ( __arglist previous parameters). Because __arglist it represents only the calling convention of a method, it is not an actual parameter.
    • A mutable parameter method can contain 0 a fixed parameter, that is, a similar method is declared void MyMethod(__arglist) .
    • __arglistcannot be used in a delegate.
The IL code of the variable parameter method

This paper introduces the variable parameter method from the perspective of C # language, and finally analyzes its IL principle.

The invocation of a Variadic method also uses the call instruction and the callvirt directive, but the parameter type needs to be explicitly specified. For example, printf("Hello %s! is %d x %c\n", __arglist("World", 6, ‘7‘)); the corresponding IL code is as follows:

Il_0000:ldstr "Hello%s! is%d x%c\n "Il_0005:ldstr" World "il_000a:ldc.i4.6il_000b:ldc.i4.s 55il_000d:call void Cyjb.testprogram::p rintf (Strin G, String, Int32, Char)

A simple explanation is to push four parameters (a fixed parameter and three variable parameters) to the stack in order, and finally call the method. The function you can see __arglist() is to expand the method parameters and populate the parameter types. Note that all four parameter types are written to IL in order to correctly invoke the method of the mutable parameter, which is also a method for what specifically provides a Ilgenerator.emitcall method to invoke a mutable parameter.

public static void printf(string format, __arglist)The IL code for the method declaration is as follows:

Note that the parameter of the method here actually has only one fixed parameter format , just one vararg more in the signature part of the method. , which indicates that the method is a mutable parameter, and the result is the same as the reflection.

There is nothing special in the method body, it is also called the correlation method of ArgIterator and TypedReference, but the arglist instruction is used to provide parameters for the ArgIterator constructor, which is the __arglist key word, and its function is Returns an unmanaged pointer to a mutable parameter list.

The above and the keywords __makeref __refvalue __reftype , respectively, correspond to the mkrefany, refanyval, and refanytype directives, which are not detailed here.

Iv. RuntimeArgumentHandle

As mentioned earlier, the delegate cannot use __arglist the keyword, then if you create a delegate for a mutable parameter method? If you look at the ArgIterator constructor, you can see that its arguments are a runtimeargumenthandle structure that contains a pointer to a parameter list of mutable parameters.

Therefore, it is entirely possible to use RuntimeArgumentHandle instead of the keyword in the method declaration __arglist , as follows:

public static void printf (string format, RuntimeArgumentHandle handle) {    ArgIterator args = new ArgIterator (handle); c3/>//other Code}

with the public static void printf(string format, __arglist) declarations have exactly the same effect, and RuntimeArgumentHandle can be used entirely anywhere.

But the printf invocation of this method is a big problem because we cannot create an instance of an effective runtimeargumenthandle struct (it does not have a constructor with parameters), and it is __arglist("World", 6, ‘7‘) not possible to use it (as can be seen from the IL code above). __arglist()the function is to expand the parameters).

To invoke such a method, you must wrap a layer containing __arglist the method:

public static void Wrap (string format, __arglist) {    printf (format, __arglist);}

in the __arglist method body, it can be considered that The keyword is an implicitly-created RuntimeArgumentHandle instances can even be used directly in RuntimeArgumentHandle handle = __arglist; this way.

This does look like a superfluous, but if you want to invoke a delegate that contains the RuntimeArgumentHandle parameter, there is only one way to do it, and the normal method is more suitable for continued use __arglist .

Variable parameter methods in C # (VarArgs)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.