我已经构建了一个自定义类型系统,用于在应用程序内的 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!经常发生这种情况,但为什么?)
- 为避免模棱两可的调用,不应在基类和派生类中重写 +、-、/、* 运算符。【那该怎么走呢?我在派生类中进行了操作重载,这需要在使用时进行强制转换,这又是丑陋的......]