Абстракция, препятствующая использованию пользовательских типов. Каким правилам следует следовать при реализации?

StackOverflow https://stackoverflow.com/questions/4072301

Вопрос

Я создал собственную систему типов для использования в сценариях C# внутри приложения.Скрипты компилируются «на лету» и позволяют взаимодействовать с внутренними данными приложения.Эта система типов спроектирована абстрактным образом с использованием таких интерфейсов, как IValue.Реализация IValue может быть RefString, RefInteger, RefDouble (среди многих других, но этого достаточно для демонстрации моей проблемы).

Вот мы и подошли к моменту, когда я застрял...Использование этих IValue объекты несколько неестественны.Считается хорошим решением всегда использовать интерфейсы для взаимодействия с объектами, но нет возможности определить неявное преобразование или перегрузку оператора для интерфейсов.Это приводит к ситуациям, когда уродливое явное приведение типов неизбежно, поэтому используется правильный оператор.

Пример:

IValue Add(IValue a, IValue b)
{
    //return a+b; // won't work: which operator +() to use?
    return (RefInteger)a + (RefInteger)b;
}

В случае C# в выражениях, включающих типы значений, предусмотрены неявные преобразования.Каков хороший способ разработки такой индивидуальной системы?

Я переписал систему типов, удалив IValue интерфейс и введение RefValue базовый класс.Таким образом уже можно устранить часть явного приведения типов.Я реализовал некоторую перегрузку операторов в этом базовом классе, но это вызвало много проблем с операторами преобразования по умолчанию...Помимо этого, логика, которую необходимо реализовать в реализации оператора, требует большого количества знаний о типах в системе.Я думаю, что в каком-то смысле это все еще тот путь, по которому нужно идти, но каковы правила, которым нужно следовать, чтобы реализовать это правильно и безопасно?

РЕДАКТИРОВАТЬ:После некоторых попыток мне удалось выяснить некоторые правила:

  • Объявляйте неявно только операторы преобразования базовых типов (int, double, string,...)
  • Объявите явным образом преобразования к базовым типам (чтобы избежать неявного приведения к int!!что случается довольно часто, но почему?)
  • Чтобы избежать неоднозначных вызовов, операторы +, -, /, * не следует переопределять в базовом классе и в производных классах.[Каков тогда путь?Я выполнил перегрузку операции в производных классах, для этого требуется приведение типов при использовании, что опять же некрасиво...]
Это было полезно?

Решение

Если вы должны быть в состоянии сделать Add операция на всех IValues, возможно, интерфейс должен включать Add метод?Тогда вы могли бы сделать return a.Add(b);, и внесите знания о том, как выполнить операцию, в каждый тип.

Одна из проблем заключается в том, что, как это выглядит сейчас, вы можете получать звонки там, где a является RefString и b является RefInteger, что, вероятно, не то, что вам нужно.Дженерики могут помочь это исправить:

T Add<T>(T a, T b) where T : IValue
{
    return a.Add(b);
}

(Конечно, вам нужно будет добавить нулевую проверку и тому подобное)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top