Позвольте мне начать жирным шрифтом:
Я прекрасно понимаю мотивацию этого и не могу понять, почему некоторые люди спорят с этим ...
То, что вы хотите, это невиртуальный специальный полиморфизм.
- ad hoc: реализация может варьироваться
- не виртуальный: по причинам производительности; отправка во время компиляции
Остальное, по-моему, сахар.
C ++ уже имеет специальный полиморфизм через шаблоны. «Понятия», однако, прояснят, какой вид специальной полиморфной функциональности используется какой-либо определяемой пользователем сущностью.
C # просто не может это сделать.
Подход, который не был бы невиртуальным : Если бы такие типы, как float, просто реализовали бы что-то вроде «INumeric» или «IAddable» (...), мы бы по крайней мере могли написать общий минимум , max, lerp и на основе этого зажима, maprange, bezier (...). Однако это не будет быстро. Вы этого не хотите.
Способы исправления:
Так как .NET в любом случае выполняет JIT-компиляцию, он также генерирует другой код для List<int>
, чем для List<MyClass>
(из-за различий между типами значений и ссылочными типами), он, вероятно, не добавит столько накладных расходов, чтобы также генерировать другой код для рекламы специальные полиморфные части.
Языку C # просто нужен способ выразить это. Один путь - это то, что вы набросали.
Другой способ - добавить ограничения типа в функцию , используя специальную полиморфную функцию:
U SuperSquare<T, U>(T a) applying{
nonvirtual operator (*) T (T, T)
nonvirtual Foo U (T)
}
{
return Foo(a * a);
}
Конечно, вы можете столкнуться с новыми и новыми ограничениями при реализации Bar, который использует Foo. Поэтому вам может понадобиться механизм присвоения имени нескольким ограничениям, которые вы регулярно используете ... Однако это опять-таки сахар, и один из способов подойти к нему - просто использовать концепцию класса типов ...
Назначение имени нескольким ограничениям похоже на определение класса типа, но я хотел бы просто рассмотреть его как своего рода механизм аббревиатур - сахар для произвольного набора ограничений типа функции:
// adhoc is like an interface: it is about collecting signatures
// but it is not a type: it dissolves during compilation
adhoc AMyNeeds<T, U>
{
nonvirtual operator (*) T (T, T)
nonvirtual Foo U (T)
}
U SuperSquare<T, U>(T a) applying AMyNeeds<T, U>
{
return Foo(a * a);
}
В каком-то месте "main" все аргументы типа известны, и все становится конкретным и может быть скомпилировано вместе.
Чего не хватает, так это отсутствия создания разных реализаций. В верхнем примере мы просто использовали полиморфные функции и дали всем знать ...
Реализация затем может идти по пути методов расширения - в их способности добавлять функциональность к любому классу в любой точке:
public static class SomeAdhocImplementations
{
public nonvirtual int Foo(float x)
{
return round(x);
}
}
В основном теперь можно написать:
int a = SuperSquare(3.0f); // 3.0 * 3.0 = 9.0 rounded should return 9
Компилятор проверяет все «невиртуальные» специальные функции, находит как встроенный оператор float (*), так и int Foo (float)
и, следовательно, может скомпилировать эту строку.
Специальный полиморфизм, конечно, идет с недостатком, который вы должны перекомпилировать для каждого типа времени компиляции, чтобы вставить правильные реализации. И, вероятно, IL не поддерживает это вложение в dll. Но, может быть, они все равно работают над этим ...
Не вижу реальной необходимости в создании конструкции класса типа.
Если что-то не получится при компиляции, мы получим ошибки ограничений или, если они были связаны вместе с помощью кодека «adhoc», сообщение об ошибке может стать еще более читабельным.
MyColor a = SuperSquare(3.0f);
// error: There are no ad hoc implementations of AMyNeeds<float, MyColor>
// in particular there is no implementation for MyColor Foo(float)
Но, конечно же, вполне возможно создание экземпляра класса типов / "интерфейса adhoc полиморфизма". Сообщение об ошибке тогда заявит: "The AMyNeeds constraint of SuperSquare has not been matched. AMyNeeds is available as StandardNeeds : AMyNeeds<float, int> as defined in MyStandardLib
".
Также было бы возможно поместить реализацию в класс вместе с другими методами и добавить «интерфейс adhoc» в список поддерживаемых интерфейсов.
Но не зависит от конкретного языкового дизайна: я не вижу недостатка в том, чтобы добавлять их так или иначе. Сохранение статически типизированных языков всегда должно выходить за границы выразительной силы, поскольку они начинались с того, что позволяли слишком мало, что, как правило, было бы меньшим набором выразительной мощности, которого нормальный программист ожидал бы возможным ...
tldr: я на вашей стороне. Подобные вещи - отстой в основных статически типизированных языках Хаскелл показал путь.