Разработка обобщенных c алгоритмов в C# - PullRequest
0 голосов
/ 26 февраля 2020

Я пишу код библиотеки 2D / 3D геометрии.

В этом примере давайте рассмотрим метод Contain, который проверяет, находится ли Line полностью внутри набора Box с. У меня есть как 2D, так и 3D реализации каждого объекта.

Сигнатуры методов будут выглядеть примерно так:

  • static bool Contains(List<Box3> boxes, Line3 line) // 3D implementation

  • static bool Contains(List<Box2> boxes, Line2 line) // 2D implementation

Эти два метода очень похожи, единственное отличие состоит в том, что мы работаем либо с набором типов (Point2, Line2, Box2), либо (Point3, Line3, Box3). Для справки, мои Point и Line классы являются структурами.

Итак, я попробовал что-то похожее на это:

static bool Contains<TB, TL, TP>(List<TB> boxes, TL line) 
    where TB: IAxisAlignedBox
    where TL: ILineSegment
    where TP: IPoint

Основная проблема в этом заключается в том, что я вызываю другой Функция, одна из следующих:

  • Point2 Intersect(Box2 box, Line2 line) // 2D implementation

  • Point3 Intersect(Box3 box, Line3 line) // 3D implementation

As объяснил мне в другом вопросе, что это не будет работать в C#, потому что он будет вызывать только TP Intersect(TB box, TL line), который не является специализированным.

Итак, мой вопрос ... как мне написать этот алгоритм в общем, без использования dynamic?

1 Ответ

3 голосов
/ 26 февраля 2020

Боюсь, C# генерики просто так не работают. Здесь нет утки, и вы не получаете различного типа generi c для каждого аргумента типа. Все разрешение типов происходит во время компиляции и не заботится о фактическом типе аргумента типа. Чтобы получить разрешение типа во время выполнения, вам нужно использовать dynamic со всеми его трудностями.

Альтернативы во многом зависят от ваших требований. Например, если у вас есть полный контроль над типами, которые можно использовать в методах Contains, вы можете выполнить собственное разрешение типов:

if (box is Box2 box2 && line is Line2 line2) return Contains(box2, line2);
else if (box is Box3 box3 && line is Line3 line3) return Contains(box3, line3);

Это, вероятно, fast что вы можете сделать (кроме того, что у вас есть конкретная c реализация для каждого случая в первую очередь). Если вы ищете что-то с меньшим обслуживанием, вы можете использовать делегаты:

static bool ContainsImpl<TB, TL, TP>(List<TB> boxes, TL line, Func<TB, TL, bool> intersect)
{
  DoAllTheStuff();
  if (intersect(box, line)) ...
}

public static Contains(List<Box2> boxes, Line2 line)=> ContainsImpl(boxes, line, Intersect);
public static Contains(List<Box3> boxes, Line3 line)=> ContainsImpl(boxes, line, Intersect);

Метод generi c содержит общий логический c для выполнения Contains и делегирует все указать c материал, основанный на фактических типах (любой пользователь вашей библиотеки будет использовать не * generi c методы). Основное преимущество заключается в том, что вы получаете полную проверку типов таким образом - вы не можете случайно забыть случай сопоставления с образцом для нового добавленного вами типа. Это, вероятно, не стоит накладных расходов делегата, учитывая, насколько простой метод пересечения. Вам нужно подумать о пользователях вашей библиотеки.

В некоторых случаях может быть целесообразно инвертировать все и использовать модель вытягивания (например, через IEnumerable<TB>) вместо нажатия. Но опять же, все это вызов, который вы должны сделать исходя из своих реальных требований. И, конечно, вы всегда можете использовать генерацию кода - иногда просто нет хорошего способа представить шаблон в C#, и такие вещи, как шаблоны T4, могут значительно помочь.

В общем, C# дженерики, ну, дженерики c. Если ограничения типа c недостаточно для того, что нужно сделать Contains, нет смысла делать его универсальным c. Вы все еще можете использовать его, чтобы избежать ненужного повторения кода, он отлично подходит для сгенерированных типов во время выполнения и применения делегатов, но он не может сделать больше, чем позволяют ограничения типов без явного приведения (или dynamic). Это не шаблоны C ++, и они не меняют C# на Python или Scala.

...