【问题标题】:Compare Delegates Action<T>比较委托 Action<T>
【发布时间】:2011-07-14 23:11:03
【问题描述】:

请随意质疑我的理智。

我需要确定 Action&lt;T&gt;Action&lt;T&gt; 是否是原始实例。我所拥有的是一个带有类变量protected Action&lt;T&gt; MessageCallback = null; 的类,当我的abstract class Message&lt;T&gt; 是通过抽象方法创建时,我强制“它们”初始化MessageCallBack。此 MessageCallback 被添加到 IList&lt;Action&lt;object&gt;&gt;。此列表中定义的每个操作都可以不同。现在,我想做的是从列表中删除一个特定的操作,但我尝试比较它失败了。

以下是我上次尝试的设置示例:

public void Unsubscribe<TMessage>(Action<TMessage> messageCallback)
    {
        var messageType = typeof(TMessage);

        var callbackTypes = messageReceivedCallbacks
            .Keys
            .Where(k => k.IsAssignableFrom(messageType));

        lock (messageReceivedCallbacks)
        {
            foreach (var callbackType in callbackTypes)
            {
                messageReceivedCallbacks[callbackType].Remove(new Action<object>(m => 
                    messageCallback((TMessage)m)));
            }
        }
    }

我知道我想做的事情可能是不可能的,但通常我只是在以不正当的方式做某事,或者缺乏适当的知识来按照我的假设去做。提前感谢您提供的任何帮助。

      • 尝试以下一些方法后更新:

比较它们总是失败。目前以下 3 条建议均无效。我确实相信我可以改变我处理这个问题的方式,并通过传递一个带有操作的键,然后指向&lt;key, indexOfAction&gt; 的单独列表然后按索引删除它来使它按我需要的方式工作。但是,我觉得我仍然需要付出很大的努力来解决,所以我将提供更多信息,看看它是否有帮助。

这里是列表:

private readonly IDictionary<Type, IList<Action<object>>> messageReceivedCallbacks;

下面是如何将动作添加到列表中:

void AddMessageReceivedCallback<TMessage>(Action<TMessage> messageReceivedCallback)
    {
        var intermediateReceivedCallback = new Action<object>(m => 
            messageReceivedCallback((TMessage)m));

        var receivedList = messageReceivedCallbacks.GetOrCreateValue(typeof(TMessage),
            () => new List<Action<object>>());
        lock (receivedList)
        {
            receivedList.Add(intermediateReceivedCallback);
        }
    }

请多多包涵,因为我对一些更高级的编码比较陌生。由于new 关键字,我可以说这阻止了我进行直接实例比较。在我(第一次)在上面发布的尝试中,我试图让我的回调与添加它的形式相匹配。这没用。我尝试比较目标、方法,甚至将它们转换为其他类型,然后进行比较。

我决定将我传递的回调转换为添加到最后一个 aka 的相同方式:

var callbackConverted = new Action<object>(m =>
                messageReceivedCallback((TMessage)m));

接下来,我使用即时窗口来获取一些信息(callback 是列表中的那个,callbackConverted 是我传入的那个):

callback.Target
{MessageBus.MessageCoordinator.<Tests.MessageBus.TestMessage>}
    messageReceivedCallback: {Method = {Void <InitializeMessageCallback>b__0(Tests.MessageBus.TestMessage)}}

callback.Method
{Void <AddMessageReceivedCallback>b__8(System.Object)}
    [System.Reflection.RuntimeMethodInfo]: {Void <AddMessageReceivedCallback>b__8(System.Object)}
    base {System.Reflection.MethodBase}: {Void <AddMessageReceivedCallback>b__8(System.Object)}
    MemberType: Method
    ReturnParameter: {Void }
    ReturnType: {Name = "Void" FullName = "System.Void"}
    ReturnTypeCustomAttributes: {Void }


callbackConverted.Target
{MessageBus.MessageCoordinator.<Tests.MessageBus.TestMessage>}
    messageReceivedCallback: {Method = {Void <InitializeMessageCallback>b__0(Tests.MessageBus.TestMessage)}}
    messageType: {Name = "TestMessage" FullName = "Tests.MessageBus.TestMessage"}

callbackConverted.Method
    {Void <Unsubscribe>b__1d(System.Object)}
        [System.Reflection.RuntimeMethodInfo]: {Void <Unsubscribe>b__1d(System.Object)}
        base {System.Reflection.MethodBase}: {Void <Unsubscribe>b__1d(System.Object)}
        MemberType: Method
        ReturnParameter: {Void }
        ReturnType: {Name = "Void" FullName = "System.Void"}
        ReturnTypeCustomAttributes: {Void }

我希望这些附加信息对您有所帮助。

      • **更新

我发现我把它弄得太复杂了。我需要做的就是添加我的操作,然后从每个字典中删除(它的唯一实例)。我正在竭尽全力做一些复杂的事情。

目前没有提供我可以说肯定有效的方法,但我正在标记我认为其他人最有可能使用的方法作为答案。感谢所有贡献的人。

【问题讨论】:

  • 那么,您正在尝试从现有的可枚举中删除“新”对象?
  • messageReceivedCallbacks 是什么类型的?请将var 更改为特定类型。
  • @jwJung:来吧,这显然是IDictionary&lt;Type, IList&lt;Action&lt;object&gt;&gt;&gt; 或者没有I 的...
  • @Timewi:你是对的!你赢得了良好的感觉。

标签: c# generics delegates comparison


【解决方案1】:

您是说要找到执行相同操作或完全相同的实例的操作? 如果它是完全相同的实例,您可以使用:

messageReceivedCallbacks[callbackType].Remove(messageCallback);

如果你想比较方法体,你可以这样做:

private bool ActionComparer<T>(Action<T> firstAction, Action<T> secondAction)
{
    if(firstAction.Target != secondAction.Target)
        return false;

    var firstMethodBody = firstAction.Method.GetMethodBody().GetILAsByteArray();
    var secondMethodBody = secondAction.Method.GetMethodBody().GetILAsByteArray();

    if(firstMethodBody.Length != secondMethodBody.Length)
        return false;

    for(var i = 0; i < firstMethodBody.Length; i++)
    {
        if(firstMethodBody[i] != secondMethodBody[i])
            return false;
    }
    return true;
}

Action<bool> actionOne = (param1) => {return;};
Action<bool> actionTwo = (param2) => {var i = 1; return;};
Action<bool> actionThree = (param1) => {return;};
Action<bool> actionFour = (param2) => {Thread.Sleep(1); return;};

var areEqualOneTwo = ActionComparer(actionOne, actionTwo);
var areEqualOneThree = ActionComparer(actionOne, actionThree);
var areEqualOneFour = ActionComparer(actionOne, actionFour);

Console.WriteLine("action one vs two: " + areEqualOneTwo);
Console.WriteLine("action one vs three: " + areEqualOneThree);
Console.WriteLine("action one vs four: " + areEqualOneFour);

结果:

没有编译器优化感谢RenniePet's comment

action one vs two: False
action one vs three: True
action one vs four: False

使用编译器优化

action one vs two: True
action one vs three: True
action one vs four: False

但是请注意,动作一和动作二的比较

【讨论】:

  • 我确实尝试过,但我相信它失败的原因不是您提供的方法。我认为它们是不同的。
  • 这是吹毛求疵,但我只想提一下,您的示例测试为 Debug 和 Release 构建提供了不同的结果。您大概将测试作为发布版本运行,并且确实产生了您显示的结果。如果您将其作为调试版本运行,则操作一与二会导致错误,因为编译器会保留无用的“var i = 1;” IL 中的声明。
  • @RenniePet 你说得对;我相信我在默认情况下启用优化时在 LINQPad 上运行了这个,并且没有注意。我将在打开和关闭优化的情况下编辑结果。谢谢
【解决方案2】:

这行得通吗?

messageReceivedCallbacks[callbackType].Remove(messageReceivedCallbacks[callbackType].FirstOrDefault(x => x.Target == messageCallback.Target && x.Method == messageCallback.Method));

【讨论】:

    【解决方案3】:

    判断两个委托是否相同,只需要比较方法和目标对象即可:

    var list = messageReceivedCallbacks[callbackType];
    for (var i = list.Count-1; i >= 0; i--)
        if (list[i].Method == messageCallback.Method && list[i].Target == messageCallback.Target)
            list.RemoveAt(i);
    

    【讨论】:

    • 每个委托都有一个或多个(目标,方法)对,您只检查第一个。
    • @Ben:我知道,接受的答案也是如此。我假设作者总是使用单方法委托。
    猜你喜欢
    • 1970-01-01
    • 2011-01-17
    • 2012-09-09
    • 2017-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-24
    • 1970-01-01
    相关资源
    最近更新 更多