明确的,具有数字类型的隐式运算符&意想不到的结果

本文关键字:运算符 意想不到 结果 数字 类型 | 更新日期: 2023-09-27 18:17:55

我从来没有做过重载操作符的大量工作,特别是隐式和显式转换。

然而,我有几个经常使用的数值形参,所以我创建了一个结构体作为数字类型的包装器来对这些形参进行强类型化。下面是一个示例实现:

public struct Parameter
{
    private Byte _value;
    public Byte Value { get { return _value; } }
    public Parameter(Byte value)
    {
        _value = value;
    }
    // other methods (GetHashCode, Equals, ToString, etc)
    public static implicit operator Byte(Parameter value)
    {
        return value._value;
    }
    public static implicit operator Parameter(Byte value)
    {
        return new Parameter(value);
    }
    public static explicit operator Int16(Parameter value)
    {
        return value._value;
    }
    public static explicit operator Parameter(Int16 value)
    {
        return new Parameter((Byte)value);
    }
}

当我在试验我的测试实现以获得显式和隐式操作符的hang时,我试图显式地将Int64转换为我的Parameter类型,令我惊讶的是它没有抛出异常,甚至更令人惊讶的是,它只是截断了数字并继续前进。我尝试排除自定义显式操作符,它仍然表现相同。

public void TestCast()
{
    try
    {
        var i = 12000000146;
        var p = (Parameter)i;
        var d = (Double)p;
        Console.WriteLine(i);   //Writes 12000000146
        Console.WriteLine(p);   //Writes 146
        Console.WriteLine(d);   //Writes 146
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);  //Code not reached
    }
}

所以我用一个普通的Byte代替我的结构重复了我的实验,并且具有相同的确切行为,所以显然这是预期的行为,但我认为一个显式的强制转换会导致数据丢失,会抛出异常。

明确的,具有数字类型的隐式运算符&意想不到的结果

当编译器分析显式用户定义的转换时,允许在"任意一侧"放置显式内置转换。(或两者)的转换。例如,如果您有一个用户定义的从int到Fred的转换,并且您有:

int? x = whatever;
Fred f = (Fred)x;

,那么编译器就会解释"有一个从int到Fred的显式转换,所以我可以从int进行显式转换?"将int转换为Fred。

在你的例子中,有一个内置的显式从长到短的转换,还有一个用户定义的显式从短到参数的转换,所以将长转换为参数是合法的。

隐式转换也是如此;编译器可以在用户定义的隐式转换的两侧插入内置隐式转换。

编译器从不链接两个用户定义的转换。

在c#中正确地构建自己的显式转换是一项艰巨的任务,我鼓励您停止尝试这样做,直到您对涵盖转换的规范的整个章节有了彻底而深刻的理解。

有关链式转换的一些有趣方面,请参阅我关于该主题的文章:

    c#中的链式用户定义显式转换c#中的链式用户定义显式转换,第二部分c#中的链式用户定义显式转换,第三部分

这个目标:

所以我创建了一个结构体作为数字类型的包装器来强类型这些参数

和这个代码:

public static implicit operator Byte(Parameter value)
{
    return value._value;
}
public static implicit operator Parameter(Byte value)
{
    return new Parameter(value);
}

完全矛盾。通过添加双向隐式操作符,您取消了包装器可能带来的任何类型安全。

因此放弃隐式转换。你可以把它们改成显式的。