为什么 Mathf.Abs(MyClass v) 在 C# 中调用隐式 int 转换而不是隐式 float 转换?

Why does Mathf.Abs(MyClass v) call an implicit int conversion rather than an implicit float conversion in C#?

我正在使用动态类型和隐式转换,我很好奇为什么以下代码调用隐式 int 转换而不是隐式 float 在我的 TestClass?

中转换
TestClass myVar = 1.6f;
var result = Mathf.Abs(myVar);

从此代码分配的值本质上是 Mathf.Abs(1) 而不是 Mathf.Abs(1.6f)

作为参考,以下是我的 TestClass,它是为此 post.

设计的 class
class TestClass
{
    public dynamic variable;

    TestClass(dynamic v)
    {
        variable = v;
    }

    public static implicit operator float(TestClass v)
    {
        return (float)v.variable;
    }

    public static implicit operator int(TestClass v)
    {
        return (int)v.variable;
    }

    public static implicit operator TestClass(float v)
    {
        return new TestClass(v);
    }
}

最终,我创建了一个 class 来反映另一种语言的功能,它允许变量为浮点数、字符串或布尔值(无需特别声明类型)并在 运行-time 取决于应用它们的运算符或函数。所讨论的语言是一种旨在帮助儿童学习编码并优雅地处理任何情况而不会崩溃的语言。这些信息并不是问题真正需要的,但提供了背景的一般概述。

首先,我们可以在不需要 Unity 或动态类型的情况下重现它。以下示例打印 1,调用 PrintValue(int) 重载:

using System;

public class Wrapper
{
    private float value;

    public Wrapper(float value) => this.value = value;

    public static implicit operator float(Wrapper wrapper) =>
        wrapper.value;

    public static implicit operator int(Wrapper wrapper) =>
        (int) wrapper.value;
}

class Test
{
    static void Main()
    {
        Wrapper wrapper = new Wrapper(1.5f);
        PrintValue(wrapper);
    }

    static void PrintValue(float f) => Console.WriteLine(f);

    static void PrintValue(int i) => Console.WriteLine(i);
}

ECMA 语言规范中描述了重载决议的规则section 12.6.4PrintValue 的两个重载都是 适用的函数成员 因为存在从 Wrapper 到每个 floatint 的隐式转换。

在这种情况下,PrintValue(int) 最终成为 更好的函数成员 ,因为从 Wrapperint 的转换是一个 从表达式 转换比从Wrapperfloat (12.6.4.4) 的转换更好,因为int 是比float 更好的转换目标( 12.6.4.6) 因为存在从 intfloat 的隐式转换,但是 不是 float 到 [=15 的隐式转换=].

换句话说:int 是一种比 float“更具体”的参数类型,就像 string 是一种“更具体”的参数类型一样参数比 object。 (因为隐式转换可用。)