Skip to content

C# 转换操作符方法

🏷️ 《CLR via C#》

转换操作符方法

CLR 规范要求转换操作符重载方法必须是 publicstatic 方法。

此外,C# 要求参数类型和返回类型二者必有其一与定义转换方法的类型相同。

cs
/// <summary>
/// 由一个 Int32 隐式构造并返回一个 Rational
/// </summary>
/// <param name="num"></param>
public static implicit operator Rational(Int32 num)
{
return new Rational(num);
}

/// <summary>
/// 由一个 Rational 显示返回一个 Int32
/// </summary>
/// <param name="r"></param>
public static explicit operator Int32(Rational r)
{
return r.ToInt32();
}

完整示例代码

cs
public sealed class Rational
{
    Single _value;

    /// <summary>
    /// 由一个 Int32 构造一个 Rational
    /// </summary>
    /// <param name="num"></param>
    public Rational (Int32 num)
    {
        _value = (Single)num;
    }

    /// <summary>
    /// 由一个 Single 构造一个 Rational
    /// </summary>
    /// <param name="num"></param>
    public Rational (Single num)
    {
        _value = num;
    }

    /// <summary>
    /// 将一个 Rational 转换成 Int32
    /// </summary>
    /// <returns></returns>
    public Int32 ToInt32()
    {
        return (Int32)_value;
    }

    /// <summary>
    /// 将一个 Rational 转换成 Single
    /// </summary>
    /// <returns></returns>
    public Single ToSingle()
    {
        return _value;
    }

    /// <summary>
    /// 由一个 Int32 隐式构造并返回一个 Rational
    /// </summary>
    /// <param name="num"></param>
    public static implicit operator Rational(Int32 num)
    {
        return new Rational(num);
    }

    /// <summary>
    /// 由一个 Single 隐式构造并返回一个 Rational
    /// </summary>
    /// <param name="num"></param>
    public static implicit operator Rational(Single num)
    {
        return new Rational(num);
    }

    /// <summary>
    /// 由一个 Rational 显示返回一个 Int32
    /// </summary>
    /// <param name="r"></param>
    public static explicit operator Int32(Rational r)
    {
        return r.ToInt32();
    }

    /// <summary>
    /// 由一个 Rational 显示返回一个 Single
    /// </summary>
    /// <param name="r"></param>
    public static explicit operator Single(Rational r)
    {
        return r.ToSingle();
    }
}

调用示例

cs
Rational r1 = 5; // Int32 隐式转型为 Rational
Rational r2 = 2.5F; // Single 隐式转型为 Rational

Int32 x = (Int32)r1; // Rational 显式转型为 Int32
Single y = (Single)r1; // Rational 显式转型为 Single

IL 代码

Rational 类 IL 代码

将对象从一种类型转换成另一种类型的方法总是叫做 op_Implicit 或者 op_Explicit 方法。

只有在转换不损失精度或者数量级的前提下,才能定义隐式转换操作符。
如果转换会造成精度或数量级损失,就应该定义一个显示转换操作符。
显示转换失败,应该让显示转换操作符方法抛出 OverflowException 或者 InvalidOperationException 异常。

查看 IL 代码
cs
.class public sealed auto ansi beforefieldinit
  ConsoleApp1.Rational
    extends [mscorlib]System.Object
{

  .field private float32 _value

  .method public hidebysig specialname rtspecialname instance void
    .ctor(
      int32 num
    ) cil managed
  {
    .maxstack 8

    // [17 9 - 17 36]
    IL_0000: ldarg.0      // this
    IL_0001: call         instance void [mscorlib]System.Object::.ctor()
    IL_0006: nop

    // [18 9 - 18 10]
    IL_0007: nop

    // [19 13 - 19 34]
    IL_0008: ldarg.0      // this
    IL_0009: ldarg.1      // num
    IL_000a: conv.r4
    IL_000b: stfld        float32 ConsoleApp1.Rational::_value

    // [20 9 - 20 10]
    IL_0010: ret

  } // end of method Rational::.ctor

  .method public hidebysig specialname rtspecialname instance void
    .ctor(
      float32 num
    ) cil managed
  {
    .maxstack 8

    // [26 9 - 26 37]
    IL_0000: ldarg.0      // this
    IL_0001: call         instance void [mscorlib]System.Object::.ctor()
    IL_0006: nop

    // [27 9 - 27 10]
    IL_0007: nop

    // [28 13 - 28 26]
    IL_0008: ldarg.0      // this
    IL_0009: ldarg.1      // num
    IL_000a: stfld        float32 ConsoleApp1.Rational::_value

    // [29 9 - 29 10]
    IL_000f: ret

  } // end of method Rational::.ctor

  .method public hidebysig instance int32
    ToInt32() cil managed
  {
    .maxstack 1
    .locals init (
      [0] int32 V_0
    )

    // [36 9 - 36 10]
    IL_0000: nop

    // [37 13 - 37 34]
    IL_0001: ldarg.0      // this
    IL_0002: ldfld        float32 ConsoleApp1.Rational::_value
    IL_0007: conv.i4
    IL_0008: stloc.0      // V_0
    IL_0009: br.s         IL_000b

    // [38 9 - 38 10]
    IL_000b: ldloc.0      // V_0
    IL_000c: ret

  } // end of method Rational::ToInt32

  .method public hidebysig instance float32
    ToSingle() cil managed
  {
    .maxstack 1
    .locals init (
      [0] float32 V_0
    )

    // [45 9 - 45 10]
    IL_0000: nop

    // [46 13 - 46 27]
    IL_0001: ldarg.0      // this
    IL_0002: ldfld        float32 ConsoleApp1.Rational::_value
    IL_0007: stloc.0      // V_0
    IL_0008: br.s         IL_000a

    // [47 9 - 47 10]
    IL_000a: ldloc.0      // V_0
    IL_000b: ret

  } // end of method Rational::ToSingle

  .method public hidebysig static specialname class ConsoleApp1.Rational
    op_Implicit(
      int32 num
    ) cil managed
  {
    .maxstack 1
    .locals init (
      [0] class ConsoleApp1.Rational V_0
    )

    // [54 9 - 54 10]
    IL_0000: nop

    // [55 13 - 55 38]
    IL_0001: ldarg.0      // num
    IL_0002: newobj       instance void ConsoleApp1.Rational::.ctor(int32)
    IL_0007: stloc.0      // V_0
    IL_0008: br.s         IL_000a

    // [56 9 - 56 10]
    IL_000a: ldloc.0      // V_0
    IL_000b: ret

  } // end of method Rational::op_Implicit

  .method public hidebysig static specialname class ConsoleApp1.Rational
    op_Implicit(
      float32 num
    ) cil managed
  {
    .maxstack 1
    .locals init (
      [0] class ConsoleApp1.Rational V_0
    )

    // [63 9 - 63 10]
    IL_0000: nop

    // [64 13 - 64 38]
    IL_0001: ldarg.0      // num
    IL_0002: newobj       instance void ConsoleApp1.Rational::.ctor(float32)
    IL_0007: stloc.0      // V_0
    IL_0008: br.s         IL_000a

    // [65 9 - 65 10]
    IL_000a: ldloc.0      // V_0
    IL_000b: ret

  } // end of method Rational::op_Implicit

  .method public hidebysig static specialname int32
    op_Explicit(
      class ConsoleApp1.Rational r
    ) cil managed
  {
    .maxstack 1
    .locals init (
      [0] int32 V_0
    )

    // [72 9 - 72 10]
    IL_0000: nop

    // [73 13 - 73 32]
    IL_0001: ldarg.0      // r
    IL_0002: callvirt     instance int32 ConsoleApp1.Rational::ToInt32()
    IL_0007: stloc.0      // V_0
    IL_0008: br.s         IL_000a

    // [74 9 - 74 10]
    IL_000a: ldloc.0      // V_0
    IL_000b: ret

  } // end of method Rational::op_Explicit

  .method public hidebysig static specialname float32
    op_Explicit(
      class ConsoleApp1.Rational r
    ) cil managed
  {
    .maxstack 1
    .locals init (
      [0] float32 V_0
    )

    // [81 9 - 81 10]
    IL_0000: nop

    // [82 13 - 82 33]
    IL_0001: ldarg.0      // r
    IL_0002: callvirt     instance float32 ConsoleApp1.Rational::ToSingle()
    IL_0007: stloc.0      // V_0
    IL_0008: br.s         IL_000a

    // [83 9 - 83 10]
    IL_000a: ldloc.0      // V_0
    IL_000b: ret

  } // end of method Rational::op_Explicit
} // end of class ConsoleApp1.Rational

调用示例 IL 代码

隐式转换调用的 op_Implicit 方法;显示转换调用的 op_Explicit 方法。

cs
// [13 13 - 13 29]
IL_0001: ldc.i4.5
IL_0002: call         class ConsoleApp1.Rational ConsoleApp1.Rational::op_Implicit(int32)
IL_0007: stloc.0      // r1

// [14 13 - 14 32]
IL_0008: ldc.r4       2.5
IL_000d: call         class ConsoleApp1.Rational ConsoleApp1.Rational::op_Implicit(float32)
IL_0012: stloc.1      // r2

// [16 13 - 16 33]
IL_0013: ldloc.0      // r1
IL_0014: call         int32 ConsoleApp1.Rational::op_Explicit(class ConsoleApp1.Rational)
IL_0019: stloc.2      // x

// [17 13 - 17 35]
IL_001a: ldloc.0      // r1
IL_001b: call         float32 ConsoleApp1.Rational::op_Explicit(class ConsoleApp1.Rational)
IL_0020: conv.r4
IL_0021: stloc.3      // y