Question

FromJava on Sat, 03 Aug 2013 11:13:14


Hi all,

Please check the following code:

class c
{
static void Main()
{
Console.WriteLine(typeof(c).GetMethod("va1").CallingConvention);
Console.WriteLine(typeof(c).GetMethod("va2").CallingConvention);
}
public static void va1 (params object[] o)
{
}
public static void va2 (__arglist)
{
}

va1 CallingConvention returns "Standard" which is not correct. va2 CallingConvention returns "VarArgs" which is correct; however, I do not want to use undocumented features. I have to get a "VarArgs" MethodInfo to call it from ILGenerator.EmitCall.


Sponsored



Replies

Wizend on Sat, 03 Aug 2013 12:53:30


va1 CallingConvention returns "Standard" which is not correct.

I think, that is absolutely compliant with the C# rule book. Below is an extract from the C# library regarding the compiler warning CA2230, that gives some hints:

"The VarArgs calling convention is used with certain method definitions that take a variable number of parameters. A method using the VarArgs calling convention is not Common Language Specification (CLS) compliant and might not be accessible across programming languages.

In C#, the VarArgs calling convention is used when a method's parameter list ends with the __arglist keyword. Visual Basic does not support the VarArgs calling convention, and Visual C++ allows its use only in unmanaged code that uses the ellipse ... notation.

To fix a violation of this rule in C#, use the params (C# Reference) keyword instead of __arglist."

Kind regards,

wizend

Elmar Boye on Sat, 03 Aug 2013 12:57:15


Hi,

why you need a VarArgs calling convention?
And C# and other managed languages as VB.NET use the params, that passes the arguments as an array, and will be null if no arguments are used.

__arglist is undocumented and just for interop with unmanaged  (C) code, see Eric Lipperts note in
http://stackoverflow.com/questions/910585/c-sharp-variable-length-args-which-is-better-and-why-arglist-params-array-o

In IL __arglist is emitted as OpCodes.Arglist. To access an argument list will have to code (or emit) a lot more, see for example UnCommon C# keywords - A Look

Regards, Elmar

FromJava on Sat, 03 Aug 2013 15:58:10


dm = new DynamicMethod ( "",typeof(void),paramstypes );
il = dm.GetILGenerator();
for(int i=0; i < paramstypes.Length; i++)
    il.Emit ( OpCodes.Ldarg,i );
il.EmitCall ( OpCodes.Call,mi,paramtypes );
il.Emit ( OpCodes.Ret );

Wizend, if you look closely to my original code, you would find I already used "params"; however, I am still getting a "Standard" MethodInfo.

Elmar, I have already mentioned that I need a "VarArgs" MethodInfo to call it from ILGenerator.EmitCall. Please check the previous code. In the code, "mi" is a MethodInfo object that must be "VarArgs"; otherwise, I get a runtime exception: "Calling Convention must be VarArgs"


Wizend on Sat, 03 Aug 2013 17:37:34


Wizend, if you look closely to my original code, you would find I already used "params"; however, I am still getting a "Standard" MethodInfo.

Yes, of course I've seen that. But, what I tried to point at and apparently failed by doing so, is that the vararg constraint is not part of the Common Lanuage Specification and the only calling convention supported by the CLS is the "standard" managed calling convention (for details, http://msdn.microsoft.com/en-us/library/12a7a7h3%28v=vs.110%29.aspx). Because using the 'params' keyword, even if it allows to use a variable number of arguments in C# similar to what the _arglist keyword allows, is supported by the CLS, its CallingConvention will always be "standard". The enum value "VarArgs" is reserved for those methods that are not CLS compliant (e.g. because of using the _arglist keyword as explained in those library article regarding the warning message CA2230 I mentioned earlier).

Elmar Boye on Sat, 03 Aug 2013 18:20:00


Hi,

I can only suggest to look at the generated IL. For a class:

    class c
    {
        internal static void Test()
        {
            Console.WriteLine(typeof(c).GetMethod("va1").CallingConvention);
            Console.WriteLine(typeof(c).GetMethod("va2").CallingConvention);
            va1(1, "b", 'c');
            CallVarArg();
        }

        public static void va1(params object[] o)
        {
            if (o != null)
            {
                for (int i = 0; i < o.Length; i++)
                    Console.WriteLine(o[i].GetType());
            }
        }

        internal static void CallVarArg()
        {
            va2(__arglist(1, "b", 'c'));
        }

        public static void va2(__arglist) 
        {
            ArgIterator iterator = new ArgIterator(__arglist);
            while (iterator.GetRemainingCount() > 0)
            {
                TypedReference tf = iterator.GetNextArg();
                Console.WriteLine(TypedReference.GetTargetType(tf));
            }
        }
    }
the generated IL for call va2(__arglist(1, "b", 'c')); is:
        .maxstack 8

	IL_0000: nop
	IL_0001: ldc.i4.1
	IL_0002: ldstr "b"
	IL_0007: ldc.i4.s 99
	IL_0009: call void ElmarBoye.Samples.c::va2(int32, string, char)
	IL_000e: nop
	IL_000f: ret

I've used mixed types to show that there is no magic on the callers site, but there is no boxing as you would see for params object. The evaluation is happening at the callee's site - but that will only happen be if the method is managed. For C(++) the VarArgs arguments must be a native type - and no System.Object for example. See also:
http://stackoverflow.com/questions/1657883/variable-number-of-arguments-in-c
(For VC++ look into vadefs.h)

Regards, Elmar

FromJava on Sun, 04 Aug 2013 08:06:34


Hi Elmar,

Since I have just started learning c#, I have some difficutly following. Please check my previous piece of code and tell me what I can do to make it work without getting the runtime exception. Simply put, how can I call a vararg method from a dynamic method?

Thank you for your time


Elmar Boye on Sun, 04 Aug 2013 13:31:10


Hi,

as I  never needed an __arglist in more then ten years of .NET programming,  I ask again:

Why you are thinking you need it?

And please show the method you are trying to call, that can help clarify the question - and if the problem really exists.

The .NET solution is always a params array as shown for va1, and that is no __arglist, just a single array parameter. The array is constructed by the caller if needed and passed as a single argument.

Regards, Elmar

FromJava on Tue, 06 Aug 2013 09:48:05


Hi Elmar,

Here is my code which produces a runtime exception.

using System;
using System.Reflection;
using System.Reflection.Emit;
public class c
{
private delegate void HelloDelegate(string msg,int i);
public static void Main()
{
DynamicMethod dm;
MethodInfo    mi;
ILGenerator   il;
HelloDelegate hd;
dm = new DynamicMethod   ( "",typeof(void),new Type[]{typeof(string),typeof(int)} );
mi = typeof(c).GetMethod ( "va1" );
il = dm.GetILGenerator ();
il.Emit     ( OpCodes.Ldarg_0 );
il.Emit     ( OpCodes.Ldarg_1 );
il.EmitCall ( OpCodes.Call, mi, new Type[]{typeof(object)} );
il.Emit     ( OpCodes.Ret );
hd = (HelloDelegate) dm.CreateDelegate ( typeof(HelloDelegate) );
hd ( "Hello, World!",120 );
}
public static void va1 ( params object[] s)
{
Console.WriteLine("good");
}
}

You said that an array must be constructed by the caller. Can you please show me how to do this. I tried to follow the Emit API for that but always gives me errors.

Thank you

Elmar Boye on Tue, 06 Aug 2013 12:52:36


Hi,

the main problem with your code is the EmitCall. That is only intended for VarArg Calls, see ILGenerator.EmitCall Mainly For vararg Methods

To call a standard method that accepts a params parameter the call is much simpler - just use Emit(Opcodes.Call):

class c
{
    internal static void Test()
    {
        Action<object[]> va1Delegate = CreateVaCall();

        var va1params = new object[] { "Hello, World!", 120 };
        va1Delegate(va1params);

        va1Delegate(new object[0]); // no arguments

        va1Delegate(new object[] { "Hello, World again!", 121 });
    }

    public static Action<object[]> CreateVaCall()
    {
        var va1MethodTypes = new Type[] { typeof(object[]) };
        var va1Method = typeof(c).GetMethod("va1");
        var va1Delegate = typeof(Action<object[]>);

        var dm = new DynamicMethod("", typeof(void), va1MethodTypes, typeof(c).Module);
        var il = dm.GetILGenerator();

        il.Emit(OpCodes.Ldarg_0);   // Load object[] parameter
        il.Emit(OpCodes.Call, va1Method);   // call (static) method
        il.Emit(OpCodes.Nop);
        il.Emit(OpCodes.Ret);
        return (Action<object[]>)dm.CreateDelegate(va1Delegate);
    }

    public static void va1(params object[] o)
    {
        if (o != null && o.Length > 0)
        {
            for (int i = 0; i < o.Length; i++)
            {
                object value = o[i];
                Console.WriteLine("va1 {0}: '{1}' ({2})", i, value, value.GetType());
            }
        }
        else
            Console.WriteLine("va1: No arguments");
    }
}

I've used an Action<object[]> as it already exists, instead of creating a new delegate type.

The method Test shows some call variants.

Regards, Elmar


FromJava on Tue, 06 Aug 2013 17:33:38


Hi Elmar,

Thank you for your time and effort. Your help is much appreciated.

I am glad that this is over, as I spent too much time struggling with this subject, specially that I am new to .NET and c#.

For my case, the problem was not only using EmitCall, but I was faced with two other problems:

1. My dynamic method gets simple parameters (not object[]) and must send those to a method that accepts "object[]".

2. Those simple parameters could be of any type: simple primitives as well as any class type.

Solution:

1. First problem: I thought that "params" means that I can send simple parameters one at a time. But it turned out that I had to package those parameters into an object array and sent this array to the method. And If I do that, I can remove the word "params" and it still works, so what is the use of it?

2. Second problem: I had to use OpCodes.Box to cast the simple parameters types to object type.

Anyway, it has been fun. Thank you again.

Elmar Boye on Wed, 07 Aug 2013 08:46:20


Hi,

as generating IL is a more advanced topic you should learn more about IL and the .NET compilers. The generated IL can be inspected using .NET Reflector or a free decompiler:
http://blog.wibeck.org/2013/02/free-options-for-reflector-net-decompiler/

1.) The "params" keyword is just a ParramArrayAttribute at IL level. And used by compilers to generate the array from a variable number of arguments. But for the CLR runtime it is just an array.

2.) As the array is of type object all value types has to boxed - that is creating a System.Object instance, see Boxing and Unboxing (C# Programming Guide).

If you look for example at the IL generated for my Test method, creating the array and the boxing is done by the C# compiler.

Regards, Elmar

FromJava on Wed, 07 Aug 2013 12:35:39


Thank you very much Elmar.