Сравнение Func на равенство. C#

Подскажите как нужно проверить следующий момент:

В методе есть параметр Func<Объект класса, object> predicate. В методе есть ветвление в зависимости от того, какой предикат буде передан. И в этом же методе я пытаюсь написать следующее:

// Предикат, равенство которому влияет на ветвление в методе
Func<Объект класса, string> func = здесь выбор нужного поля класса(к примеру, s => s.Name). 

// Пытаюсь сравнить предикат из аргумента и func(это не правильно, как правильно не догоняю)
if (predicate.Equals(func))
{
    flag = true;
}

Подскажите как это нужно делать?


Ответы (1 шт):

Автор решения: MSDN.WhiteKnight

Сравнение делегатов работает следующим образом: https://docs.microsoft.com/en-us/dotnet/api/system.delegate.equals?view=netcore-3.1

  • If the two methods being compared are both static and are the same method on the same class, the methods are considered equal and the targets are also considered equal.

  • If the two methods being compared are instance methods and are the same method on the same object, the methods are considered equal and the targets are also considered equal.

  • Otherwise, the methods are not considered to be equal and the targets are also not considered to be equal.

Увы, два разных лямбда выражения вида s => s.Name, даже если в коде они идентичны, представляются в бинарнике разными анонимными методами, так что сравнение делегатов работать не будет.

Но если методы делают одно и то же, должен же быть способ выяснить это, иными словами, сравнить методы "по значению"? Да, такой способ есть. Так как лямбда-метод - это одиночное выражение, ни блоков обработки исключений, ни локальных переменных в нем быть не может, равенство таких методов, по сути, сводится к побайтовому равенству их IL-кода. Это приводит нас к такому способу сравнения:

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reflection;

public class Person
{
    public string Name;
    public string Location;
}

class Program
{
    static bool AreMethodsEqual(MethodBase left, MethodBase right)
    {
        MethodBody m1 = left.GetMethodBody();
        MethodBody m2 = right.GetMethodBody();
        byte[] il1 = m1.GetILAsByteArray();
        byte[] il2 = m2.GetILAsByteArray();
        return il1.SequenceEqual(il2);
    }

    static void Main(string[] args)
    {
        Func<Person, string> func = s => s.Name;
        Func<Person, string> predicate = x => x.Name;
        
        Console.WriteLine("Результат сравнения делегатов: "+func.Equals(predicate));
        Console.WriteLine("Результат сравнения методов:   "+AreMethodsEqual(func.Method, predicate.Method));

        Console.ReadKey();
    }
}

Но этот способ работает только для предикатов, полученных из лямбда-выражений, заданных на этапе разработки. Если оно генерируется динамически на основе LINQ Expression, AreMethodsEqual не сработает, так как GetMethodBody выдаст исключение.

→ Ссылка