I would like to know what is the value of an out/ref parameter of a invoked method.
When the method is invoked without throws an exception, the value is received in the parameter, but I do not get the value when an exception throws in the invoked method. Invoking directly the method without Reflection, the value is received.
Am I doing something wrong or is this a .net limitation?
using System;
using System.Reflection;
class Program
{
static void Main()
{
string[] arguments = new string[] { bool.FalseString, null };
MethodInfo method = typeof(Program).GetMethod("SampleMethod");
try
{
method.Invoke(null, arguments);
Console.WriteLine(arguments[1]); // arguments[1] = "Hello", Prints Hello
arguments = new string[] { bool.TrueString, null };
method.Invoke(null, arguments);
}
catch (Exception)
{
Console.WriteLine(arguments[1]); // arguments[1] = null, Does not print
}
arguments[1] = null;
try
{
SampleMethod(bool.TrueString, out arguments[1]);
}
catch (Exception)
{
Console.WriteLine(arguments[1]); // arguments[1] = "Hello"
}
}
public static void SampleMethod(string throwsException, out string text)
{
text = "Hello";
if (throwsException == bool.TrueString)
throw new Exception("Test Exception");
}
}
After search a litlle bit I found the solution below. Would be good to use it?
using System;
using System.Reflection;
using System.Reflection.Emit;
public static class MethodInfoExtension
{
public static object InvokeStrictly(this MethodInfo source, object obj, object[] parameters)
{
ParameterInfo[] paramInfos = source.GetParameters();
if ((parameters == null) || (paramInfos.Length != parameters.Length))
{
throw new ArgumentException();
}
Type[] paramTypes = new[] { typeof(object[]) };
DynamicMethod invokerBuilder = new DynamicMethod(string.Empty, typeof(object), paramTypes);
ILGenerator ilGenerator = invokerBuilder.GetILGenerator();
Label exBlockLabel = ilGenerator.BeginExceptionBlock();
for (int i = 0; i < paramInfos.Length; i++)
{
var paramInfo = paramInfos[i];
bool paramIsByRef = paramInfo.ParameterType.IsByRef;
var paramType = paramIsByRef ? paramInfo.ParameterType.GetElementType() : paramInfo.ParameterType;
ilGenerator.DeclareLocal(paramType);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldc_I4, i);
ilGenerator.Emit(OpCodes.Ldelem_Ref);
Label label1 = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Brfalse, label1);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldc_I4, i);
ilGenerator.Emit(OpCodes.Ldelem_Ref);
ilGenerator.Emit(OpCodes.Unbox_Any, paramType);
ilGenerator.Emit(OpCodes.Stloc_S, (byte)i);
ilGenerator.MarkLabel(label1);
if (paramIsByRef)
{
ilGenerator.Emit(OpCodes.Ldloca_S, (byte)i);
}
else
{
ilGenerator.Emit(OpCodes.Ldloc_S, (byte)i);
}
}
LocalBuilder resultLocal = ilGenerator.DeclareLocal(typeof(object), false);
ilGenerator.Emit(OpCodes.Call, source);
if (source.ReturnType == typeof(void))
{
ilGenerator.Emit(OpCodes.Ldnull);
}
ilGenerator.Emit(OpCodes.Stloc_S, resultLocal);
ilGenerator.Emit(OpCodes.Leave, exBlockLabel);
ilGenerator.BeginFinallyBlock();
for (int i = 0; i < paramInfos.Length; i++)
{
var paramInfo = paramInfos[i];
bool paramIsByRef = paramInfo.ParameterType.IsByRef;
var paramType = paramIsByRef ? paramInfo.ParameterType.GetElementType() : paramInfo.ParameterType;
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldc_I4, i);
ilGenerator.Emit(OpCodes.Ldloc_S, (byte)i);
if (paramType.IsValueType)
{
ilGenerator.Emit(OpCodes.Box, paramType);
}
ilGenerator.Emit(OpCodes.Stelem, typeof(object));
}
ilGenerator.EndExceptionBlock();
ilGenerator.Emit(OpCodes.Ldloc_S, resultLocal);
ilGenerator.Emit(OpCodes.Ret);
var invoker = (Func<object[], object>)invokerBuilder.CreateDelegate(typeof(Func<object[], object>));
return invoker(parameters);
}
}
public class Program
{
static void Main()
{
object[] args = new object[1];
try
{
MethodInfo targetMethod = typeof(Program).GetMethod("Method");
targetMethod.InvokeStrictly(null, args);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.WriteLine();
}
Console.WriteLine(args[0]);
Console.ReadLine();
}
public static void Method(out string arg)
{
arg = "Hello";
throw new Exception("Test Exception");
}
}