简体   繁体   中英

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

I'm using dynamic types and implicit conversions and am curious why the following code calls the implicit int conversion and not the implicit float conversion within my TestClass ?

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

The value assigned from this code is essentially Mathf.Abs(1) rather than Mathf.Abs(1.6f) .

For reference, the following is my TestClass which is a contrived class for the purpose of this post.

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);
    }
}

Ultimately I'm creating a class to mirror the functionality of another language which allows variables to be float, string or boolean (without specifically declaring the type) and casts appropriately at run-time depending on the operator or function to which they're being applied. The language in question is one designed to help children learn to code and handles any situation gracefully without crashing. This information isn't really required for the question but gives a general overview of the background.

Firstly, we can reproduce this without needing Unity or dynamic typing. The following example prints 1, calling the PrintValue(int) overloads:

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);
}

The rules of overload resolution are described in the ECMA language specification section 12.6.4 . Both overloads of PrintValue are applicable function members because there's an implicit conversion from Wrapper to each of float and int .

In this case, PrintValue(int) ends up as the better function member because the conversion from Wrapper to int is a better conversion from expression than the conversion from Wrapper to float (12.6.4.4) because int is a better conversion target than float (12.6.4.6) because there's an implicit conversion from int to float , but there isn't an implicit conversion from float to int .

To put it another way: int is a sort of "more specific" parameter type than float , in the same way that string would be a "more specific" parameter than object . (Because of the implicit conversions available.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM