Я создал пользовательскую систему типов для использования в скриптах 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, что происходит довольно часто, но почему?)
- Чтобы избежать неоднозначных вызовов, операторы +, -, /, * не должны быть переопределены в базовый класс и производные классы. [Каков путь? Я сделал операцию перегрузки в производных классах, это требует кастинг на использование, что опять-таки некрасиво ...]
Я до сих пор есть проблемы с преобразованием типа по умолчанию, если я делаю 'IVALUE Ь = 6; 'например. Это не может быть объявлено в интерфейсе и работает, если у меня есть оператор неявного преобразования в классе 'RefInteger'.Идея привлечения дженериков может спасти меня от некоторых неприятностей, спасибо за намек. Операторы типа +, *, /, - не могут быть объявлены в интерфейсах, или я что-то пропустил? – jdehaan
@jdehaan: Нет, насколько я знаю, нет способа объявить операторов в интерфейсе. –
Я превратил свой проект в использование конкретного класса с использованием некоторых разрешенных имплицитных преобразований и принуждения для других, так же как и числовое продвижение по стандарту ECMA 334. Похоже, что он, по крайней мере, обладает хорошим читаемым кодом и отвечает требованиям преобразования типов. Спасибо за вашу помощь, я принял ваш ответ, в конце концов, вы были правы в том смысле, что нет оптического способа решения проблемы на уровне интерфейса. – jdehaan