Перегрузка метода без изменения классов - PullRequest
1 голос
/ 27 марта 2019

У меня есть доступ к структуре классов, которую я не могу изменить, следующим образом:

Graphics
  Circle
  Line
  etc.

Опять же, я не могу изменить это! Все они имеют индивидуальные свойства, такие как Radius, FirstPoint, LastPoint и т. Д., А также некоторые общие свойства.

Я хочу создать метод, который принимает объект Graphics и, в зависимости от типа объекта, будет запускать метод ToJson:

Graphics g = db.GetGraphic(123);
// Console.WriteLine(g.GetType()) prints "Circle"

// Should run some specific implementation for `Circle` type graphics, and
// have an overload for all types including Graphics
ToJson(g);

Сначала я подумал, что могу хитро перегрузить метод ToJson:

ToJson(Graphics g) { ... }
ToJson(Circle g) { ... }
ToJson(Line g) { ... }

однако это, разумеется, касается общей перегрузки ToJson(Graphics) каждый раз.

Я уверен, что мог бы сделать что-то вроде следующего:

if (g is Circle) ...
if (g is Line) ...
if (g is Graphics) ...

или создайте словарь, чтобы уменьшить сложность для каждого типа, но это не самый лучший способ сделать что-либо


Что я рассмотрел

Я подумал, есть ли какой-нибудь универсальный метод-обертка, который я мог бы использовать вокруг каждого объекта (например, new JsonGraphics(g).ToJson()), но я не хочу выполнять какую-либо ручную проверку типа самостоятельно.

Я посмотрел на шаблон двойной отправки и посетителя, но я не был уверен, что они удовлетворяют моим требованиям, поскольку они выглядят так, как будто я должен изменить эти классы (или, может быть, я просто не до конца их понял), и (вроде очевидно, однако) дженерики также в основном находятся за окном, поскольку требуют, чтобы я заранее знал, что это за тип Graphics.


Два вопроса:

Есть ли лучший способ сделать это, кроме как использовать какой-нибудь словарь или что-то другое if (g is Type) -подобное?

Если бы я мог изменить классы, как бы выглядел шаблон? В этом случае это невозможно, но лучше ли использовать двойную рассылку / посетителя в случае, если я могу?

1 Ответ

2 голосов
/ 27 марта 2019

Не имея возможности изменить базовый класс или иметь доступ к конкретному типу, прежде чем он превратится в общий тип Graphics, к сожалению, я не думаю, что вы можете что-то сделать, кроме как проверить тип времени выполнения Graphics object.

Вы можете использовать оператор switch (начиная с C # 7.0), который немного чище, чем ваша if цепочка:

switch (g)
{
    case Circle circle: ... break;
    case Line line: ... break;
    default: /* Oh no! */ break;
}

Лично я не вижу большого преимущества виспользуя словарь над оператором switch, как это - оба могут быть помещены в небольшой автономный метод (и, таким образом, уменьшите количество нарушения принципа открытия / закрытия), но переключение будет значительнодешевле.

Вы также можете использовать dynamic, что приводит к позднему связыванию во время выполнения:

dynamic d = g;
ToJson(d); // Picks the right ToJson overload corresponding to the runtime type of 'd'

... хотя, динамический имеет довольно большие затраты времени выполнения и обычносчитается запахом.

...