C#传递参数-动态事件订阅

C#传递参数-动态事件订阅,c#,.net,events,delegates,observer-pattern,C#,.net,Events,Delegates,Observer Pattern,我的控制器中有以下内容,并希望将float类型的x、y和z传递给订阅者。然而,我在理解上有些困难。我需要调整什么才能将浮动(x,y,z)作为参数传递?多谢各位 private void ProcessEvents() { if(Input.GetMouseButtonDown(1)) { // Data to be passed to subscribers float x = Input.mousePosition.x; floa

我的控制器中有以下内容,并希望将float类型的x、y和z传递给订阅者。然而,我在理解上有些困难。我需要调整什么才能将浮动(x,y,z)作为参数传递?多谢各位

private void ProcessEvents()
{
    if(Input.GetMouseButtonDown(1))
    {
        // Data to be passed to subscribers
        float x = Input.mousePosition.x;
        float y = Input.mousePosition.y;
        float z = Input.mousePosition.z;

        var publisher  = new EventPublisher();
        var handler = new Handler();

        // Void delegate with one parameter
        string eventName = "RightClickEvent";
        var rightClickEvent = publisher.GetType().GetEvent(eventName);

        rightClickEvent.AddEventHandler(publisher, EventProxy.Create<int>(rightClickEvent, i=>Debug.LogError(i + "!")));

        publisher.PublishEvents();
    }
}
事件发布者:

public class EventPublisher
{
    public event EventHandler<ExampleEventArgs> RightClickEvent;

    /// <summary>
    /// Publishes the events.
    /// </summary>
    public void PublishEvents()
    {
        if(RightClickEvent != null) 
        {
            RightClickEvent(this, new ExampleEventArgs{IntArg = 5});
        }
    }
}
EventProxy:

static class EventProxy
{ 
    // Void delegate with one parameter
    static public Delegate Create<T>(EventInfo evt, Action<T> d)
    {
        var handlerType = evt.EventHandlerType;
        var eventParams = handlerType.GetMethod("Invoke").GetParameters();

        //lambda: (object x0, ExampleEventArgs x1) => d(x1.IntArg)
        var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x")).ToArray();
        var arg    = getArgExpression(parameters[1], typeof(T));
        var body   = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"), arg);
        var lambda = Expression.Lambda(body,parameters);
        return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
    }

    // Returns an expression that represents an argument to be passed to the delegate
    static Expression getArgExpression(ParameterExpression eventArgs, Type handlerArgType)
    {
        if(eventArgs.Type == typeof(ExampleEventArgs) && handlerArgType == typeof(int))
        {
            //"x1.IntArg"
            var memberInfo = eventArgs.Type.GetMember("IntArg")[0];
            return Expression.MakeMemberAccess(eventArgs,memberInfo);
        }
        throw new NotSupportedException(eventArgs + "->" + handlerArgType);
    }

    // Void delegates with no parameters
    static public Delegate Create(EventInfo evt, Action d)
    { 
        var handlerType = evt.EventHandlerType;
        var eventParams = handlerType.GetMethod("Invoke").GetParameters();

        //lambda: (object x0, EventArgs x1) => d()
        var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x"));
        var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"));
        var lambda = Expression.Lambda(body,parameters.ToArray());
        return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
    }
}
静态类EventProxy
{ 
//具有一个参数的Void委托
静态公共委托创建(EventInfo evt,操作d)
{
var handlerType=evt.EventHandlerType;
var eventParams=handlerType.GetMethod(“调用”).GetParameters();
//lambda:(对象x0,ExampleEventArgs x1)=>d(x1.IntArg)
var parameters=eventParams.Select(p=>Expression.Parameter(p.ParameterType,“x”)).ToArray();
var arg=getArgExpression(参数[1],类型(T));
var body=Expression.Call(Expression.Constant(d),d.GetType().GetMethod(“Invoke”),arg);
var lambda=表达式.lambda(主体,参数);
返回Delegate.CreateDelegate(handlerType,lambda.Compile(),“Invoke”,false);
}
//返回表示要传递给委托的参数的表达式
静态表达式GetTargetExpression(ParameterExpression eventArgs,类型handlerArgType)
{
if(eventArgs.Type==typeof(例如eventArgs)和&handlerArgType==typeof(int))
{
//“x1.IntArg”
var memberInfo=eventArgs.Type.GetMember(“IntArg”)[0];
返回表达式.MakeMemberAccess(eventArgs、memberInfo);
}
抛出新的NotSupportedException(eventArgs+“->”+handlerArgType);
}
//没有参数的无效委托
静态公共委托创建(EventInfo evt,操作d)
{ 
var handlerType=evt.EventHandlerType;
var eventParams=handlerType.GetMethod(“调用”).GetParameters();
//lambda:(对象x0,事件参数x1)=>d()
var parameters=eventParams.Select(p=>Expression.Parameter(p.ParameterType,“x”);
var body=Expression.Call(Expression.Constant(d),d.GetType().GetMethod(“Invoke”);
var lambda=Expression.lambda(body,parameters.ToArray());
返回Delegate.CreateDelegate(handlerType,lambda.Compile(),“Invoke”,false);
}
}

我添加了一些代码,可以满足您的要求:

我将
getArgExpression
函数修改为

// Returns a List of expressions that represent the arguments to be passed to the delegate
static IEnumerable<Expression> getArgExpression(ParameterExpression eventArgs, Type handlerArgType)
{
    if (eventArgs.Type == typeof(ExampleEventArgs) && handlerArgType == typeof(int))
    {
        //"x1.IntArg"
        var memberInfo = eventArgs.Type.GetMember("IntArg")[0];
        return new List<Expression> { Expression.MakeMemberAccess(eventArgs, memberInfo) };
    }
    if (eventArgs.Type == typeof(MousePositionEventArgs) && handlerArgType == typeof(float))
    {
        //"x1.X"
        var xMemberInfo = eventArgs.Type.GetMember("X")[0];
        //"x1.Y"
        var yMemberInfo = eventArgs.Type.GetMember("Y")[0];
        //"x1.Z"
        var zMemberInfo = eventArgs.Type.GetMember("Z")[0];
        return new List<Expression>
                   {
                       Expression.MakeMemberAccess(eventArgs, xMemberInfo),
                       Expression.MakeMemberAccess(eventArgs, yMemberInfo),
                       Expression.MakeMemberAccess(eventArgs, zMemberInfo),
                   };
    }
    throw new NotSupportedException(eventArgs + "->" + handlerArgType);
}
此外,我还在“EventProxy”中编写了一个新函数,该函数将处理具有3个相同类型参数的委托

// Void delegate with three parameters
static public Delegate Create<T>(EventInfo eventInformation, Action<T, T, T> lambdaDelegate)
{
    var handlerType = eventInformation.EventHandlerType;
    var eventParams = handlerType.GetMethod("Invoke").GetParameters();

    //lambda: (object x0, ExampleEventArgs x1) => d(x1.X,x1.Y,x1.Z)
    var parameters = eventParams.Select(p => Expression.Parameter(p.ParameterType, "x")).ToArray();
    var arg = getArgExpression(parameters[1], typeof(T));
    var body = Expression.Call(Expression.Constant(lambdaDelegate), lambdaDelegate.GetType().GetMethod("Invoke"), arg);
    var lambda = Expression.Lambda(body, parameters);
    return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
}
//具有三个参数的Void委托
静态公共委托创建(事件信息、事件信息、操作lambdaDelegate)
{
var handlerType=eventInformation.EventHandlerType;
var eventParams=handlerType.GetMethod(“调用”).GetParameters();
//lambda:(对象x0,ExampleEventArgs x1)=>d(x1.X,x1.Y,x1.Z)
var parameters=eventParams.Select(p=>Expression.Parameter(p.ParameterType,“x”)).ToArray();
var arg=getArgExpression(参数[1],类型(T));
var body=Expression.Call(Expression.Constant(lambdaDelegate),lambdaDelegate.GetType().GetMethod(“Invoke”),arg);
var lambda=表达式.lambda(主体,参数);
返回Delegate.CreateDelegate(handlerType,lambda.Compile(),“Invoke”,false);
}
现在,我们可以使用以下代码轻松添加MouseeEvent订阅

rightClickEvent.AddEventHandler(publisher, EventProxy.Create<float>(rightClickEvent, (t, u, v) => Console.Write(t + "x" + u + "x" + v + "!")));
rightClickEvent.AddEventHandler(publisher,EventProxy.Create(rightClickEvent,(t,u,v)=>Console.Write(t+“x”+u+“x”+v+”));
就设计而言,此解决方案不是最好的,它使用了您展示的当前设计,但我对使用此方法维护可扩展且易于遵循的体系结构持强烈保留意见。 我建议您创建一个适配器/转换器,该适配器/转换器将从EventArgs类生成lambda参数,通过实现一个接口,可以轻松地在EventArgs中指定这些参数,这样getArgExpression将只使用转换器生成适当的参数,并且您可以去掉许多
if(Type==typeof)(…)


我需要更多的时间来制定解决方案,但如果你真的感兴趣,我可以试着写下来,然后发布。

好的,所以我读了你所说的以类似MVC的风格解耦应用程序模块。我通常喜欢使用强类型代码,即使是在使用反射时,但我对MVC比较陌生,不知道e推荐做法。你比我更了解你的要求,所以我只是编辑了Nguyen的解决方案,因为我相信他使用了一些文件中未包含的扩展名,并将结果发布在这里。所有功劳都归他所有


很抱歉回复太晚,但您似乎在别处找到了解决方案:),祝您好运。

非常感谢您的回复。我目前正在尽最大努力改进我的活动实施,如果能为我提供更好的解决方案,我将不胜感激。谢谢。在联机搜索时,我看到您的代码片段取自,此实现要求您可以使用eventname注册事件处理程序。如果你能给我一些关于你到底想要实现什么的细节,我可以想出一个更适合你需要的解决方案。您是否只需要提出并处理mouseeventarg,还是需要针对多个通用场景的解决方案?这确实是一个片段。我的目标是创建一个通用的解决方案,该解决方案将为多种情况接受类似类型的参数。例如,MouseEventArgs和WorldPositionArgs都采用X、Y、Z浮动。希望这能对您有所帮助,再次感谢您的帮助。简单地解释一下我正在尝试做的事情:我的目标是使我的应用程序的每个功能保持解耦。特性以类似MVC的模式构建,其控制器充当入口点。功能控制器负责订阅主应用程序事件。我正在给我的主应用程序添加一个突出显示功能
public class MousePositionEventArgs : EventArgs
{
    public float X { get; set; }
    public float Y { get; set; }
    public float Z { get; set; }
}
// Void delegate with three parameters
static public Delegate Create<T>(EventInfo eventInformation, Action<T, T, T> lambdaDelegate)
{
    var handlerType = eventInformation.EventHandlerType;
    var eventParams = handlerType.GetMethod("Invoke").GetParameters();

    //lambda: (object x0, ExampleEventArgs x1) => d(x1.X,x1.Y,x1.Z)
    var parameters = eventParams.Select(p => Expression.Parameter(p.ParameterType, "x")).ToArray();
    var arg = getArgExpression(parameters[1], typeof(T));
    var body = Expression.Call(Expression.Constant(lambdaDelegate), lambdaDelegate.GetType().GetMethod("Invoke"), arg);
    var lambda = Expression.Lambda(body, parameters);
    return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
}
rightClickEvent.AddEventHandler(publisher, EventProxy.Create<float>(rightClickEvent, (t, u, v) => Console.Write(t + "x" + u + "x" + v + "!")));
namespace Dynamics
{
    public static class DynamicHandler
    {
        /// <summary>
        /// Invokes a static delegate using supplied parameters.
        /// </summary>
        /// <param name="targetType">The type where the delegate belongs to.</param>
        /// <param name="delegateName">The field name of the delegate.</param>
        /// <param name="parameters">The parameters used to invoke the delegate.</param>
        /// <returns>The return value of the invocation.</returns>
        public static object InvokeDelegate(this Type targetType, string delegateName, params object[] parameters)
        {
            return ((Delegate)targetType.GetField(delegateName).GetValue(null)).DynamicInvoke(parameters);
        }

        /// <summary>
        /// Invokes an instance delegate using supplied parameters.
        /// </summary>
        /// <param name="target">The object where the delegate belongs to.</param>
        /// <param name="delegateName">The field name of the delegate.</param>
        /// <param name="parameters">The parameters used to invoke the delegate.</param>
        /// <returns>The return value of the invocation.</returns>
        public static object InvokeDelegate(this object target, string delegateName, params object[] parameters)
        {
            return ((Delegate)target.GetType().GetField(delegateName).GetValue(target)).DynamicInvoke(parameters);
        }

        /// <summary>
        /// Adds a dynamic handler for a static delegate.
        /// </summary>
        /// <param name="targetType">The type where the delegate belongs to.</param>
        /// <param name="fieldName">The field name of the delegate.</param>
        /// <param name="func">The function which will be invoked whenever the delegate is invoked.</param>
        /// <returns>The return value of the invocation.</returns>
        public static Type AddHandler(this Type targetType, string fieldName,
            Func<object[], object> func)
        {
            return InternalAddHandler(targetType, fieldName, func, null, false);
        }

        /// <summary>
        /// Adds a dynamic handler for an instance delegate.
        /// </summary>
        /// <param name="target">The object where the delegate belongs to.</param>
        /// <param name="fieldName">The field name of the delegate.</param>
        /// <param name="func">The function which will be invoked whenever the delegate is invoked.</param>
        /// <returns>The return value of the invocation.</returns>
        public static Type AddHandler(this object target, string fieldName,
            Func<object[], object> func)
        {
            return InternalAddHandler(target.GetType(), fieldName, func, target, false);
        }

        /// <summary>
        /// Assigns a dynamic handler for a static delegate or event.
        /// </summary>
        /// <param name="targetType">The type where the delegate or event belongs to.</param>
        /// <param name="fieldName">The field name of the delegate or event.</param>
        /// <param name="func">The function which will be invoked whenever the delegate or event is fired.</param>
        /// <returns>The return value of the invocation.</returns>
        public static Type AssignHandler(this Type targetType, string fieldName,
            Func<object[], object> func)
        {
            return InternalAddHandler(targetType, fieldName, func, null, true);
        }

        /// <summary>
        /// Assigns a dynamic handler for a static delegate or event.
        /// </summary>
        /// <param name="target">The object where the delegate or event belongs to.</param>
        /// <param name="fieldName">The field name of the delegate or event.</param>
        /// <param name="func">The function which will be invoked whenever the delegate or event is fired.</param>
        /// <returns>The return value of the invocation.</returns>
        public static Type AssignHandler(this object target, string fieldName, Func<object[], object> func)
        {
            return InternalAddHandler(target.GetType(), fieldName, func, target, true);
        }

        private static Type InternalAddHandler(Type targetType, string fieldName,
            Func<object[], object> func, object target, bool assignHandler)
        {
            Type delegateType;
            var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic |
                               (target == null ? BindingFlags.Static : BindingFlags.Instance);
            var eventInfo = targetType.GetEvent(fieldName, bindingFlags);
            if (eventInfo != null && assignHandler)
                throw new ArgumentException("Event can be assigned.  Use AddHandler() overloads instead.");

            if (eventInfo != null)
            {
                delegateType = eventInfo.EventHandlerType;
                var dynamicHandler = BuildDynamicHandler(delegateType, func);
                eventInfo.GetAddMethod(true).Invoke(target, new Object[] { dynamicHandler });
            }
            else
            {
                var fieldInfo = targetType.GetField(fieldName);
                                                    //,target == null ? BindingFlags.Static : BindingFlags.Instance);
                delegateType = fieldInfo.FieldType;
                var dynamicHandler = BuildDynamicHandler(delegateType, func);
                var field = assignHandler ? null : target == null
                                ? (Delegate)fieldInfo.GetValue(null)
                                : (Delegate)fieldInfo.GetValue(target);
                field = field == null
                            ? dynamicHandler
                            : Delegate.Combine(field, dynamicHandler);
                if (target != null)
                    target.GetType().GetField(fieldName).SetValue(target, field);
                else
                    targetType.GetField(fieldName).SetValue(null, field);
                    //(target ?? targetType).SetFieldValue(fieldName, field);
            }
            return delegateType;
        }

        /// <summary>
        /// Dynamically generates code for a method whose can be used to handle a delegate of type 
        /// <paramref name="delegateType"/>.  The generated method will forward the call to the
        /// supplied <paramref name="func"/>.
        /// </summary>
        /// <param name="delegateType">The delegate type whose dynamic handler is to be built.</param>
        /// <param name="func">The function which will be forwarded the call whenever the generated
        /// handler is invoked.</param>
        /// <returns></returns>
        public static Delegate BuildDynamicHandler(this Type delegateType, Func<object[], object> func)
        {
            var invokeMethod = delegateType.GetMethod("Invoke");
            var parameters = invokeMethod.GetParameters().Select(parm =>
                Expression.Parameter(parm.ParameterType, parm.Name)).ToArray();
            var instance = func.Target == null ? null : Expression.Constant(func.Target);
            var convertedParameters = parameters.Select(parm => Expression.Convert(parm, typeof(object))).Cast<Expression>().ToArray();
            var call = Expression.Call(instance, func.Method, Expression.NewArrayInit(typeof(object), convertedParameters));
            var body = invokeMethod.ReturnType == typeof(void)
                ? (Expression)call
                : Expression.Convert(call, invokeMethod.ReturnType);
            var expr = Expression.Lambda(delegateType, body, parameters);
            return expr.Compile();
        }
    }
}
class TestClass 
{
    private void Test()
    {
        TestInstance();
        TestStatic();
    }

    private void TestInstance()
    {
        var eventClass = new EventClass();
        eventClass.InvokeDelegate("InstanceEventRaiseDelegate");
        eventClass.AddHandler("InstanceEvent", parameters =>
            {
                return true;
            });
        eventClass.AddHandler("InstanceEventRaiseDelegate", parameters =>
        {
            return true;
        });
        eventClass.InvokeDelegate("InstanceEventRaiseDelegate");

        eventClass.AssignHandler("InstanceEventRaiseDelegate", parameters =>
        {
            return true;
        });
        eventClass.InvokeDelegate("InstanceEventRaiseDelegate");
    }

    private void TestStatic()
    {
        typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate");
        typeof(EventClass).AddHandler("StaticEvent", parameters =>
        {
            return true;
        });
        typeof(EventClass).AddHandler("StaticEventRaiseDelegate", parameters =>
        {
            return true;
        });
        typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate");
        typeof(EventClass).AssignHandler("StaticEventRaiseDelegate", parameters =>
        {
            return true;
        });
        typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate");
    }

    public class EventClass
    {

        #region Instance

        public EventClass()
        {
            InstanceEventRaiseDelegate = OnInstanceEvent;
        }

        public Action InstanceEventRaiseDelegate;

        public event EventHandler InstanceEvent;

        public void OnInstanceEvent()
        {
            if (InstanceEvent != null)
                InstanceEvent(this, EventArgs.Empty);
        }

        #endregion

        #region Static

        static EventClass()
        {
            StaticEventRaiseDelegate = OnStaticEvent;
        }

        public static Action StaticEventRaiseDelegate;

        public static event EventHandler StaticEvent;

        public static void OnStaticEvent()
        {
            if (StaticEvent != null)
                StaticEvent(null, EventArgs.Empty);
        }

        #endregion
    }
}