Сравнить действие делегатов <T> - PullRequest
10 голосов
/ 15 июля 2011

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

Мне нужно определить, является ли Action<T> против 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));

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

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

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

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

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

Ответы [ 3 ]

7 голосов
/ 15 июля 2011

Вы говорите о поиске действия, которое делает то же самое, или точно такой же экземпляр?Если это тот же самый экземпляр, который вы можете просто использовать:

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 голосов
/ 15 июля 2011

Будет ли это работать?

messageReceivedCallbacks[callbackType].Remove(messageReceivedCallbacks[callbackType].FirstOrDefault(x => x.Target == messageCallback.Target && x.Method == messageCallback.Method));
0 голосов
/ 15 июля 2011

Чтобы определить, являются ли два делегата одинаковыми, вам нужно только сравнить метод и целевой объект:

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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...