Здравствуйте, Слава, Вы писали:
V>>Т.е., те фишки ФП, которые не руинят ООП — они в практику дотнета таскаются с удовольствием, смотрю. Остальные практики игнорятся, хотя именно такая реализация параметрического полиморфизма требует именно таких практик. ))
С>Я вот не понимаю, о какой технике речь. Поясните, пожалуйста. На примере.
"Словарь операций" умеет работать с некими типами или классом типов. Для статического ресолвинга в дотнете словарь может быть только value-типом.
Возьмем тот же компаратор и метод Sort(IComparer<T> comparer) какого-нить List<T>, изменим сигнатуру метода:
public class List<T> {
public void Sort<Comparer>(Comparer comparer) where Comparer : IComparer<T> {
...
}
...
}
В этом случае можно реализовать Comparer для произвольного типа T, причем сам тип T можно не нагружать реализацией интерфейса IComparable<T>. Если Comparer будет выполнен как value-type, то JIT будет вынужден сгенерировать уникальную реализацию метода Sort, в итоге мы избавляемся от вызовов методов интерфейса IComparer<T>, т.е. от лишней динамики, начинает работать статика и прочий инлайнинг.
Т.е., суть техники в том, что можно разработать некий тип и рядом с ним "словарь" операций над ним.
Схематично:
interface IMath<T>
{
T Add(T a, T b);
T Sub(T a, T b);
T Mul(T a, T b);
T Div(T a, T b);
}
struct VulgarFraction {
public int Numerator, Denominator;
public struct Operations : IMath<VulgarFraction>
, IEqualityComparer<VulgarFraction>
, IComparer<VulgarFraction>
, ...
{
public VulgarFraction Mul(VulgarFraction a, VulgarFraction b) {
return new VulgarFraction { Numerator = a.Numerator*b.Numerator, Denominator = a.Denominator*b.Denominator };
}
...
}
}
Далее. Самих таких "словарей операций" для некоего типа может быть более одного. Например, для простой дроби — с упрощением после каждой операции или без упрощения. С проверкой на переполнение int или без и т.д.
Далее один из таких словарей можно подавать на методы, организованные по принципу доработанной сигнатуры Sort выше.
Например, для встроенных числовых типов дотнета нет никаких ограничений, выраженных в виде интерфейса, позволяющих использовать математические операции, но можно нарисовать для каждого интересующего типа тот самый "словарь операций", реализующий IMath<> и, таким образом, разрабатывать эффективные математические generic-алгоритмы. Разве что потребуется каждый раз подавать этот словарь как аргумент — либо как обычный аргумент, либо как генерик параметр примерно так:
public class OurExtentions {
public T Average<Operations, T>(IEnumerable<T> collection) where Operations : IMath<T>, new() {
Operations ops = new Operations();
...
sum = ops.Add(sum, item);
...
}
}
Ну или можно представить некий сложный алгоритм как объект (структуру) и параметризировать этот объект словарём лишь однажды.