Сравнить Делегаты Action <T>

Не стесняйтесь подвергать сомнению мое здравомыслие.

Мне нужно определить, является ли исходный экземпляр Action<T> vs Action<T>. У меня есть класс с переменной класса protected Action<T> MessageCallback = null;, когда мой abstract class Message<T> создается с помощью абстрактного метода, который я принудительно "им" инициализирует MessageCallBack. Этот MessageCallback добавляется к IList<Action<object>>. Каждое действие, определенное в этом списке, может быть другим. Теперь, что я хочу сделать, это удалить определенное действие из списка, но я не могу попытаться его сравнить.

Ниже приведен образец последней настройки, которую я попытался:

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 рекомендаций ниже не работает. Я верю, что могу изменить, как я обрабатываю это, и заставить его работать, как мне это нужно, передав ключ с действием, которое затем указывает на отдельный список <key, indexOfAction>, а затем удалив его по индексу. Тем не менее, я чувствую, что мне все еще нужно приложить все усилия, чтобы решить эту проблему, поэтому я собираюсь дать немного больше информации, чтобы узнать, помогает ли она.

Вот список:

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));

Затем я использовал непосредственное окно, чтобы просто получить некоторую информацию (обратный вызов - это список в списке, а 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 }

Я надеюсь, что эта дополнительная информация поможет.

      • ** ОБНОВЛЕНИЕ

Я понял, что делаю это слишком сложным. Все, что мне нужно было сделать, было ключевым дополнением моего действия, а затем удалить (единственный экземпляр этого) из каждого словаря. Я ушел с пути, чтобы сделать что-то сложное.

В настоящее время не предоставляется какой-либо метод. Я могу сказать, что все работает, но я отмечаю тот, который, как я чувствую, у других будет лучший снимок использования в качестве ответа. Спасибо всем, кто внес вклад.

Ответ 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

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

Обратите внимание, однако, что сравнение между действиями один и два

Ответ 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);