В C# как да получите различен екземпляр на IEqualityComparer‹T›, ако общият тип имплементира интерфейс

Имам общ метод с неограничен общ тип. Трябва да получа различен IEqualityComparer, ако тип T внедрява интерфейс.

Един код изглежда приблизително така:

public SomeState MergeCollection<T>(
    ICollection<T> thisValue, 
    ICollection<T> otherValue, 
    SomeStrategy strategy = SomeStrategy.Default, 
    IEqualityComparer<T> comparer = null)
{
    comparer = comparer ?? GetComparer<T>(strategy);
    // code using comparer
}

public IEqualityComparer<T> GetComparer<T>(SomeStrategy strategy)
{
    if(typeof(IMerge).IsAssignableFrom(typeof(T)))
    {
        return GetMergeComparer<T>(strategy);
    }
    else
    {
        return EqualityComparer<T>.Default;
    }
}

Изглежда като типичен случай за специализация на шаблони, но C# не поддържа такава функция, доколкото знам. Мислех също за статичен клас, който прилага стратегически модел, за да размени функцията за прихващане на сравнителя, но статичният ще бъде приложен за всички типове, така че е необходимо търсене в речника на тип метод. Генерирането на IL код за това изглежда е последна мярка и не си струва усилието, като се има предвид, че кодът не се използва толкова много.

Кой е най-добрият начин да промените поведението на въпросния метод, така че да получи специален случай, ако T имплементира IMerge? Питам, защото теоретично трябва да е възможно да се дефинира поведение по време на компилиране или поне по време на инициализация (статичен конструктор), ако то няма да се промени по време на изпълнение.


person too    schedule 13.10.2015    source източник
comment
Какъв е проблемът със съществуващия ви код? Докато можете да кеширате typeof(IMerge).IsAssignableFrom(typeof(T))) в статичен ConditionalWeakDictionary, това едва ли ще си струва труда и вероятно няма да подобри производителността толкова много.   -  person CodesInChaos    schedule 13.10.2015
comment
Добавих описание в края. Той използва отражение, за да промени поведението, което е известно по време на компилиране и следователно по принцип не се счита за добър код.   -  person too    schedule 13.10.2015
comment
Веднъж за тип изглежда добър компромис. Проблемът е, че самият клас не е общ, а само методът. Предполагам, че това ще изисква създаване на отделен общ фабричен клас за статичен компаратор със свойство за прихващане на компаратора. Изглежда трудно :)   -  person too    schedule 13.10.2015


Отговори (1)


  1. Не мисля, че има нещо нередно в съществуващия ви код. Лично аз бих го запазил такъв, какъвто е.

    IMO оптимизирането на това, преди да имате доказателства, че е критично за производителността, е преждевременна оптимизация.

  2. Ако наистина искате да кеширате резултата от сравнението, можете да използвате нещо като:

    static class MergeComparerHelper<T>
    {
        static bool SupportsMerge = typeof(IMerge).IsAssignableFrom(typeof(T));
    }
    

    Това използва факта, че всяка генерична инстанция получава свои собствени статични полета. Можете да използвате частен вложен клас за това.

    Обърнете внимание обаче, че това все още носи разходите за изпращане по време на изпълнение, тъй като текущата реализация на .NET JITter не произвежда специализирани реализации за различни референтни типове Ts.

person CodesInChaos    schedule 13.10.2015
comment
Ще оставя 1 такъв, какъвто е, но 2 изглежда като разумен подход, който определено ще запазя с отметка за по-сложни / нуждаещи се от повече оптимизация специални случаи на генерични специализации на клас/метод. Той също така показва ограниченията на C# в тази област. Ще изчакам с приемането няколко дни за всеки случай. Благодаря за помощта. - person too; 13.10.2015