这是一个最小的可重现示例:
// Implicit and explicit conversions are overloadable operators:
Note note = new Note (9);
Console.WriteLine(note);
Note n = (Note)554.37;
double x = n;
Console.WriteLine(n);
Console.WriteLine(x);
public struct Note
{
int value;
public int SemitonesFromA { get { return value; } }
public Note (int semitonesFromA) { value = semitonesFromA; }
// Convert to hertz
public static implicit operator double (Note x) => 440 * Math.Pow (2, (double)x.value / 12);
// Convert from hertz (accurate to the nearest semitone)
public static explicit operator Note (double x) =>
new Note ((int)(0.5 + 12 * (Math.Log (x / 440) / Math.Log (2))));
public override string ToString() => $"{SemitonesFromA} semitones from A";
}
我本以为
9 semitones from A
4 semitones from A
554.3652619537442
但我明白
739.9888454232688
554.3652619537442
554.3652619537442
我认为这是因为在 Console.WriteLine 的“优先级顺序”中,隐式转换为 double 比在 Object 类型上调用 .ToString() 具有更高的优先级。这是如何工作的?如何通过查看文档(https://learn.microsoft.com/en-us/dotnet/api/system.console.writeline?view=net-8.0#overloads)来知道选择了哪些重载?
它只是选择“最接近的”匹配。
您期望隐式
Console.Writeline
调用ToString
。但是,如果它具有适合该类型的正确重载,则不会。并
Console.WriteLine
推断出正确的重载,因为Note
可以隐式转换为double
,然后Console.WriteLine(double)
精确匹配重载,并且不会继续调用具有object?
参数类型的一般重载,而只会调用ToString
方法。您可以将鼠标悬停在您的呼叫上
Console.WriteLine
并查看将使用什么过载:如果你将隐式运算符更改为显式运算符,则会得到所期望的结果: