C# 在使用IL时,有没有办法将固定长度的参数转换为_arglist?

C# 在使用IL时,有没有办法将固定长度的参数转换为_arglist?,c#,dynamic,il,C#,Dynamic,Il,这是我第一次使用IL,我正在创建一种透明代理。代码可以正常工作,但还有一些地方需要改进。首先,当我截取一个方法时,它可以有任意数量或类型的参数。理想情况下,我想使用_arglist或类似的东西。我尝试使用EmitCall,但结果是一个空的参数列表。因此,我只是传入一个占位符字符串,并使用它来识别参数的结尾,如下所示: private object ProxyMethod(object methodName, object arg1, object arg2, object arg3, objec

这是我第一次使用IL,我正在创建一种透明代理。代码可以正常工作,但还有一些地方需要改进。首先,当我截取一个方法时,它可以有任意数量或类型的参数。理想情况下,我想使用_arglist或类似的东西。我尝试使用EmitCall,但结果是一个空的参数列表。因此,我只是传入一个占位符字符串,并使用它来识别参数的结尾,如下所示:

private object ProxyMethod(object methodName, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15)
{
      var args = new List<object>(new[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, EmptyArgPlaceHolder })
        .TakeWhile(i => i.ToString() != EmptyArgPlaceHolder).ToArray();
      // etc
私有对象代理方法(对象方法名、对象arg1、对象arg2、对象arg3、对象arg4、对象arg5、对象arg6、对象arg7、对象arg8、对象arg9、对象arg10、对象arg11、对象arg12、对象arg13、对象arg14、对象arg15)
{
var args=新列表(新[]{arg1、arg2、arg3、arg4、arg5、arg6、arg7、arg8、arg9、arg10、arg11、arg12、arg13、arg14、arg15、EmptyArgPlaceHolder})
.TakeWhile(i=>i.ToString()!=EmptyArgPlaceHolder).ToArray();
//等
这太可怕了

此外,我尝试尽可能少地使用反射,但我仍然必须使用MethodInfo。每次调用代理上的方法时,都会创建MethodInfo的实例,然后使用提供的参数调用MethodInfo。我想知道的是MethodInfo的实际创建速度慢还是调用慢?如果是创建的,那么是否值得根据MethodInfo维护一个方法名称字典,这样我只需创建每个MethodInfo一次

另外,如果您发现任何其他可以改进的地方,请告诉我,因为我说过这是我第一次使用动态方法

谢谢

以下是完整的代码:

public class WcfProxy
{
    private const string EmptyArgPlaceHolder = "EMPTY\u0007";

    private readonly Type _interfaceType = typeof(ICmsDataServiceWcf);
    private readonly AssemblyBuilder _assemblyBuilder;
    private readonly ModuleBuilder _moduleBuilder;
    private TypeBuilder _typeBuilder;

    private WcfProxy()
    {
        // Get the app domain and initialize our own assembly with it
        var appDomain = Thread.GetDomain();
        var assemblyName = new AssemblyName { Name = "ReflectionHelperAsm" };

        // All shared types get initiated on construction
        _assemblyBuilder = appDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
        _moduleBuilder = _assemblyBuilder.DefineDynamicModule("ReflectionHelperDynDll", "WcfProxy.dll", true);
    }

    private static WcfProxy _instance;
    public static WcfProxy Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new WcfProxy();
            }
            return _instance;

        }
    }

    #region Type Building Code
    // Some of this code might be slow but it only gets run once

    private Type CreateType()
    {
        _typeBuilder = _moduleBuilder.DefineType("ICmsDataServiceWcfProxy",
                                                 TypeAttributes.Public | TypeAttributes.Class);
        _typeBuilder.AddInterfaceImplementation(_interfaceType);
        CreateConstructor();
        CreateMethods();
        return _typeBuilder.CreateType();
    }

    private void CreateConstructor()
    {
        var constructor = typeof(object).GetConstructor(new Type[0]);
        var constructorBuilder = _typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes);
        ILGenerator ilGenerator = constructorBuilder.GetILGenerator();
        ilGenerator.Emit(OpCodes.Ldarg_0);  // Load this
        ilGenerator.Emit(OpCodes.Call, constructor); // Call object's constructor
        ilGenerator.Emit(OpCodes.Ret);
    }

    private void CreateMethods()
    {
        var proxyMethod = this.GetType().GetMethod("ProxyMethod");
        // Find all of the methods that need to be implemented (we have no properties etc) and implement them
        foreach (var mi in _interfaceType.GetMethods())
        {
            var paramTypes = mi.GetParameters().Select(p => p.ParameterType);
            // Define the method and copy the properties from the interface method
            var methodBuilder = _typeBuilder.DefineMethod(mi.Name, MethodAttributes.Virtual | MethodAttributes.Public, mi.ReturnType, paramTypes.ToArray());

            var il = methodBuilder.GetILGenerator();
            // In the body of this method we call the proxy method
            EmitProxyMethodCall(il, proxyMethod, mi);

            if (mi.ReturnType.IsValueType)
            {
                // If it is a primitive type then unbox it to the correct type? Is that right?
                il.Emit(OpCodes.Unbox_Any, mi.ReturnType);
            }
            else
            {
                // If it is a class then cast it to the correct type
                il.Emit(OpCodes.Castclass, mi.ReturnType);
            }

            il.Emit(OpCodes.Ret); // End of method
            _typeBuilder.DefineMethodOverride(methodBuilder, mi); // Override the interface mthod 
        }
    }

    private void EmitProxyMethodCall(ILGenerator il, MethodInfo proxyMethod, MethodInfo realMethod)
    {
        il.Emit(OpCodes.Ldarg_0); // Load the class's pointer 
        var realParams = realMethod.GetParameters();
        var proxyParams = proxyMethod.GetParameters();

        // Setup ProxyMethod's paramaters
        il.Emit(OpCodes.Ldstr, realMethod.Name); // First param is always the name of the real method
        for (var i = 0; i < proxyParams.Length - 1; i++) // We -1 because we have already populated one above
        {
            if (i < realParams.Length)
            {
                il.Emit(OpCodes.Ldarg, i + 1);
                // Load the argument passed in to this method on to the stack to be passed in to the proxy method. +1 because Ldarg_0 is the class itself
                il.Emit(OpCodes.Box, realParams[i].ParameterType); // Set the type
            }
            else
            {
                il.Emit(OpCodes.Ldstr, EmptyArgPlaceHolder); //TODO: This is ugly as hell - need to fix it. 
                //We use the bell character because it is seldom used in other strings
            }
        }

        il.Emit(OpCodes.Call, proxyMethod); // Call proxy method with the paramaters above
    }

    #endregion

    private ICmsDataServiceWcf _proxy;
    public ICmsDataServiceWcf Proxy
    {
        get
        {
            if (_proxy == null)
            {
                // Only create this once since it is quite intensive
                Type type = CreateType();
                _proxy = (ICmsDataServiceWcf)Activator.CreateInstance(type);
            }
            return _proxy;
        }
    }

    private static ChannelFactory<ICmsDataServiceWcf> _factory;
    public static ICmsDataServiceWcf GetDataService()
    {
        //TODO: Move this to helper
        string url = ConfigurationManager.AppSettings["service_url"];
        EndpointAddress endPoint = new EndpointAddress(url);

        //TODO: Should I close and create a new factory every now and then or is it ok to keep it open?

        if (_factory == null || _factory.State == CommunicationState.Faulted || _factory.State == CommunicationState.Closed) 
        {
            if (_factory != null)
            {
                _factory.Abort();
            }
            var binding = new WSHttpBinding(SecurityMode.None)
                {
                    Security = {Mode = SecurityMode.None},
                    MaxReceivedMessageSize = 52428800,
                    CloseTimeout = new TimeSpan(0, 1, 0),
                    OpenTimeout = new TimeSpan(0, 1, 0),
                    ReceiveTimeout = new TimeSpan(0, 10, 0),
                    SendTimeout = new TimeSpan(0, 1, 0),
                    MaxBufferPoolSize = int.MaxValue,
                    ReaderQuotas =
                        {
                            MaxStringContentLength = int.MaxValue,
                            MaxDepth = int.MaxValue,
                            MaxArrayLength = int.MaxValue,
                            MaxBytesPerRead = int.MaxValue,
                            MaxNameTableCharCount = int.MaxValue
                        }
                };

            _factory = new ChannelFactory<ICmsDataServiceWcf>(binding, endPoint);

            foreach (OperationDescription op in _factory.Endpoint.Contract.Operations)
            {
                var dataContractBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
                if (dataContractBehavior != null)
                {
                    dataContractBehavior.MaxItemsInObjectGraph = int.MaxValue;
                }
            }
        }

        var channel = _factory.CreateChannel();
        return channel;
    }

    private object ProxyMethod(object methodName, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, object arg11, object arg12, object arg13, object arg14, object arg15)
    {
        MethodInfo method = _interfaceType.GetMethod(methodName.ToString()); //TODO: Is this slow? Is there a better way to do it?
        //TODO: This is a horrible hack - need to find out how to turn fixed length params in to and __arglist or params object[] and then pass it in here
        var args = new List<object>(new[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, EmptyArgPlaceHolder })
            .TakeWhile(i => i.ToString() != EmptyArgPlaceHolder).ToArray();

        object result; 
        // More code goes here

        var realInstance = GetDataService();
        try
        {
            result = method.Invoke(realInstance, args);
            ((IChannel)realInstance).Close();
        }
        catch (Exception ex)
        {
            // If the channel is faulted then abort it - see here http://www.codeproject.com/Articles/74129/The-Proper-Use-and-Disposal-of-WCF-Channels-or-Com
            ((IChannel)realInstance).Abort();
            if (ex is TargetInvocationException)
            {
                throw ex.InnerException;
            }
            throw;
        }
        return result;
    }
}
公共类WcfProxy
{
私有常量字符串EmptyArgPlaceHolder=“EMPTY\u0007”;
私有只读类型_interfaceType=typeof(ICmsDataServiceWcf);
私有只读汇编生成器_AssemblyBuilder;
私有只读ModuleBuilder\u ModuleBuilder;
私人打字机(TypeBuilder);;
私有WcfProxy()
{
//获取应用程序域并用它初始化我们自己的程序集
var appDomain=Thread.GetDomain();
var assemblyName=newassemblyname{Name=“ReflectionHelperAsm”};
//所有共享类型都会在构造时启动
_assemblyBuilder=appDomain.DefinedDynamicAssembly(assemblyName,AssemblyBuilderAccess.RunAndSave);
_moduleBuilder=_assemblyBuilder.DefinedDynamicModule(“ReflectionHelperDynDll”,“WcfProxy.dll”,true);
}
私有静态WcfProxy_实例;
公共静态WcfProxy实例
{
得到
{
if(_instance==null)
{
_实例=新的WcfProxy();
}
返回_实例;
}
}
#区域类型建筑规范
//这些代码中的一些可能很慢,但只运行一次
私有类型CreateType()
{
_typeBuilder=_moduleBuilder.DefineType(“ICmsDataServiceWcfProxy”,
TypeAttributes.Public | TypeAttributes.Class);
_typeBuilder.AddInterfaceImplementation(_interfaceType);
CreateConstructor();
CreateMethods();
返回_typeBuilder.CreateType();
}
私有void CreateConstructor()
{
var constructor=typeof(object).GetConstructor(新类型[0]);
var constructorBuilder=_typeBuilder.DefineConstructor(MethodAttributes.Public,CallingConventions.Standard,Type.EmptyTypes);
ILGenerator ILGenerator=constructorBuilder.GetILGenerator();
ilGenerator.Emit(操作码.Ldarg_0);//加载此
ilGenerator.Emit(OpCodes.Call,构造函数);//调用对象的构造函数
ilGenerator.Emit(操作码.Ret);
}
私有void CreateMethods()
{
var proxyMethod=this.GetType().GetMethod(“proxyMethod”);
//找到所有需要实现的方法(我们没有属性等),并实现它们
foreach(在_interfaceType.GetMethods()中的var mi)
{
var paramTypes=mi.GetParameters().Select(p=>p.ParameterType);
//定义方法并从接口方法复制属性
var methodBuilder=_typeBuilder.DefineMethod(mi.Name,MethodAttributes.Virtual | MethodAttributes.Public,mi.ReturnType,paramTypes.ToArray());
var il=methodBuilder.GetILGenerator();
//在这个方法的主体中,我们称之为代理方法
EmitProxyMethodCall(il、proxyMethod、mi);
if(mi.ReturnType.IsValueType)
{
//如果它是基元类型,那么将其取消装箱到正确的类型?对吗?
il.Emit(操作码.Unbox_Any,mi.ReturnType);
}
其他的
{
//如果是类,则将其强制转换为正确的类型
il.Emit(操作码.Castclass,mi.ReturnType);
}
il.Emit(OpCodes.Ret);//方法结束
_typeBuilder.DefineMethodOverride(methodBuilder,mi);//重写接口mthod
}
}
私有void EmitProxyMethodCall(ILGenerator il、MethodInfo proxyMethod、MethodInfo realMethod)
{
il.Emit(OpCodes.Ldarg_0);//加载类的指针
var realParams=realMethod.GetParameters();
var proxyParams=proxyMethod.GetParameters();
//设置ProxyMethod的参数
il.Emit(OpCodes.Ldstr,realMethod.Name);//第一个参数始终是实际方法的名称
对于(var i=0;i