Абстракция, препятствующая использованию пользовательских типов, каковы правила, которым нужно следовать в реализации? - PullRequest
0 голосов
/ 01 ноября 2010

Я создал собственную систему типов для использования в сценариях 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 !! что происходит довольно часто, но почему?)
  • Во избежание неоднозначных вызовов операторы +, -, /, * не должны быть переопределены в базовом классе и в производных классах. [Каков путь тогда? Я сделал операцию перегрузки в производных классах, это требует приведения на использование, что опять-таки некрасиво ...]

1 Ответ

1 голос
/ 01 ноября 2010

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

Одна проблема состоит в том, что, как это выглядит сейчас, вы можете получать вызовы, где a равно RefStringb - это RefInteger, что, вероятно, не то, что вы хотите.Обобщения могут помочь исправить это:

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

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

...