C#中Mathf.Abs传入自定义类实例为何优先触发隐式int而非float转换
根本原因
这个现象是C#的重载决议规则和隐式转换优先级共同导致的,具体逻辑如下:
Mathf.Abs方法存在多个重载,你传入的参数类型是TestClass,没有任何Abs重载直接接收该类型,因此编译器会检查所有Abs的参数类型,筛选出TestClass支持隐式转换的目标类型,这里你实现了到int和float的隐式转换,所以会筛选出两个候选重载:Mathf.Abs(int)Mathf.Abs(float)
- 接下来编译器要在两个候选重载里选最优解,规则是:如果从源类型到参数类型T1的转换,比到T2的转换「更好」,就选T1对应的重载。判断「更好」的标准之一是:如果T1可以隐式转换为T2,而T2不能隐式转换为T1,那么到T1的转换更优。
- C#中
int可以隐式转换为float,但float不能隐式转换为int(会丢失精度,需要显式强转),因此编译器会判定TestClass到int的转换更优,优先选择Mathf.Abs(int)重载,自然就调用了你写的隐式int转换运算符,最终得到的是截断小数后的1的绝对值。
可选解决方案
方案1:调用时显式指定转换类型
最直接的方式,调用时强制转成float,就会命中float的重载:
var result = Mathf.Abs((float)myVar);
方案2:调整隐式转换的设计
如果你的业务场景里绝大多数情况优先用float类型计算,可以把int的隐式转换改成显式转换,这样编译器筛选候选重载的时候就不会把Mathf.Abs(int)纳入候选,自然就会选float的版本:
// 把int的隐式转换修改为显式转换 public static explicit operator int(TestClass v) { return (int)v.variable; }
方案3:用dynamic做运行时重载决议
因为你要做的是动态类型的少儿编程语言,本身就需要运行时根据存储的值类型适配转换,可以把变量声明为dynamic类型,这样重载决议会在运行时执行,会根据你内部存储的variable的实际类型选择最匹配的重载:
dynamic myVar = new TestClass(1.6f); var result = Mathf.Abs(myVar); // 运行时会识别到内部是float,调用隐式float转换,命中Abs(float)
内容的提问来源于stack exchange,提问作者Rok
相关产品推荐
相关产品推荐

