2010-11-01 3 views
0

Я создал пользовательскую систему типов для использования в скриптах 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. Таким образом, уже часть явных отбросов может быть устранена. Я реализовал некоторые из перегрузок операторов в этом базовом классе, но это вызвало большие проблемы с операторами преобразования по умолчанию ... Кроме того, логика реализации в реализации оператора подразумевает много знаний о типах в системе. Я думаю, что это как-то все еще так, как нужно идти, но каковы правила, которые следует соблюдать, чтобы реализовать это в хорошем и безопасном виде?

EDIT: После борьбы в то время как некоторые из правил, которые я мог бы выяснить, являются:

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

ответ

1

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

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

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

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

+0

Я до сих пор есть проблемы с преобразованием типа по умолчанию, если я делаю 'IVALUE Ь = 6; 'например. Это не может быть объявлено в интерфейсе и работает, если у меня есть оператор неявного преобразования в классе 'RefInteger'.Идея привлечения дженериков может спасти меня от некоторых неприятностей, спасибо за намек. Операторы типа +, *, /, - не могут быть объявлены в интерфейсах, или я что-то пропустил? – jdehaan

+0

@jdehaan: Нет, насколько я знаю, нет способа объявить операторов в интерфейсе. –

+0

Я превратил свой проект в использование конкретного класса с использованием некоторых разрешенных имплицитных преобразований и принуждения для других, так же как и числовое продвижение по стандарту ECMA 334. Похоже, что он, по крайней мере, обладает хорошим читаемым кодом и отвечает требованиям преобразования типов. Спасибо за вашу помощь, я принял ваш ответ, в конце концов, вы были правы в том смысле, что нет оптического способа решения проблемы на уровне интерфейса. – jdehaan

Смежные вопросы