什么时候应该在C#中定义一个(显式或隐式)转换运算符?

前端之家收集整理的这篇文章主要介绍了什么时候应该在C#中定义一个(显式或隐式)转换运算符?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
C#有点鲜为人知的功能是创建隐式或显式 user-defined type conversions的可能性.
我已经写了6年的C#代码,我从来没有使用过.所以,恐怕我可能会失去好的机会.

什么是合法的,良好的用户定义的转换?你有没有比只定义自定义方法更好的例子?

原来,微软有关于转化的design guidelines,其中最相关的是:

Do not provide a conversion operator if such conversion is not clearly
expected by the end users.

但是什么时候是转换“预期”?在玩具类课外,我无法找出任何真实的用例.

以下是答案中提供的示例的摘要

> Radians / Degrees / double
>极地/ Point2D
>开尔文/ Farenheit /摄氏度

该模式似乎是:隐式转换大多数(只有?)在定义数值/值类型时有用,转换由公式定义.回想起来,这是很明显的.不过,我想知道非数字类是否也可以受益于隐式转换?

解决方法

如在评论中提到的,度数和旋转是避免混淆双重值的一个很好的例子,特别是在API之间.

我拉出了我们目前正在使用的Radians和Degrees课程.现在看看它们(很久以后),我想清理它们(特别是评论/文档),并确保它们经过适当的测试.幸运的是,我已经设法在调度时间上做到这一点.无论如何,使用这些,您自己承担风险,我不能保证这里的所有数学是否正确,因为我很确定我们没有实际使用/测试我们写的所有功能.

弧度

/// <summary>
/// Defines an angle in Radians
/// </summary>
public struct Radians
{
    public static readonly Radians ZERO_PI = 0;
    public static readonly Radians ONE_PI = System.Math.PI;
    public static readonly Radians TWO_PI = ONE_PI * 2;
    public static readonly Radians HALF_PI = ONE_PI * 0.5;
    public static readonly Radians QUARTER_PI = ONE_PI * 0.25;

    #region Public Members

    /// <summary>
    /// Angle value
    /// </summary>
    public double Value;
    /// <summary>
    /// Finds the Cosine of the angle
    /// </summary>
    public double Cos
    {
        get
        {
            return System.Math.Cos(this);
        }
    }
    /// <summary>
    /// Finds the Sine of the angle
    /// </summary>
    public double Sin
    {
        get
        {
            return System.Math.Sin(this);
        }
    }

    #endregion

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="value">angle value in radians</param>
    public Radians(double value)
    {
        this.Value = value;
    }
    /// <summary>
    /// Gets the angle in degrees
    /// </summary>
    /// <returns>Returns the angle in degrees</returns>
    public Degrees GetDegrees()
    {
        return this;
    }

    public Radians Reduce()
    {
        double radian = this.Value;
        bool IsNegative = radian < 0;
        radian = System.Math.Abs(radian);
        while (radian >= System.Math.PI * 2)
        {
            radian -= System.Math.PI * 2;
        }
        if (IsNegative && radian != 0)
        {
            radian = System.Math.PI * 2 - radian;
        }
        return radian;
    }

    #region operator overloading

    /// <summary>
    /// Conversion of Degrees to Radians
    /// </summary>
    /// <param name="deg"></param>
    /// <returns></returns>
    public static implicit operator Radians(Degrees deg)
    {
        return new Radians(deg.Value * System.Math.PI / 180);
    }
    /// <summary>
    /// Conversion of integer to Radians
    /// </summary>
    /// <param name="i"></param>
    /// <returns></returns>
    public static implicit operator Radians(int i)
    {
        return new Radians((double)i);
    }
    /// <summary>
    /// Conversion of float to Radians
    /// </summary>
    /// <param name="f"></param>
    /// <returns></returns>
    public static implicit operator Radians(float f)
    {
        return new Radians((double)f);
    }
    /// <summary>
    /// Conversion of double to Radians
    /// </summary>
    /// <param name="dbl"></param>
    /// <returns></returns>
    public static implicit operator Radians(double dbl)
    {
        return new Radians(dbl);
    }
    /// <summary>
    /// Conversion of Radians to double
    /// </summary>
    /// <param name="rad"></param>
    /// <returns></returns>
    public static implicit operator double(Radians rad)
    {
        return rad.Value;
    }
    /// <summary>
    /// Add Radians and a double
    /// </summary>
    /// <param name="rad"></param>
    /// <param name="dbl"></param>
    /// <returns></returns>
    public static Radians operator +(Radians rad,double dbl)
    {
        return new Radians(rad.Value + dbl);
    }
    /// <summary>
    /// Add Radians to Radians
    /// </summary>
    /// <param name="rad1"></param>
    /// <param name="rad2"></param>
    /// <returns></returns>
    public static Radians operator +(Radians rad1,Radians rad2)
    {
        return new Radians(rad1.Value + rad2.Value);
    }
    /// <summary>
    /// Add Radians and Degrees
    /// </summary>
    /// <param name="rad"></param>
    /// <param name="deg"></param>
    /// <returns></returns>
    public static Radians operator +(Radians rad,Degrees deg)
    {
        return new Radians(rad.Value + deg.GetRadians().Value);
    }
    /// <summary>
    /// Sets Radians value negative
    /// </summary>
    /// <param name="rad"></param>
    /// <returns></returns>
    public static Radians operator -(Radians rad)
    {
        return new Radians(-rad.Value);
    }
    /// <summary>
    /// Subtracts a double from Radians
    /// </summary>
    /// <param name="rad"></param>
    /// <param name="dbl"></param>
    /// <returns></returns>
    public static Radians operator -(Radians rad,double dbl)
    {
        return new Radians(rad.Value - dbl);
    }
    /// <summary>
    /// Subtracts Radians from Radians
    /// </summary>
    /// <param name="rad1"></param>
    /// <param name="rad2"></param>
    /// <returns></returns>
    public static Radians operator -(Radians rad1,Radians rad2)
    {
        return new Radians(rad1.Value - rad2.Value);
    }
    /// <summary>
    /// Subtracts Degrees from Radians
    /// </summary>
    /// <param name="rad"></param>
    /// <param name="deg"></param>
    /// <returns></returns>
    public static Radians operator -(Radians rad,Degrees deg)
    {
        return new Radians(rad.Value - deg.GetRadians().Value);
    }


    #endregion

    public override string ToString()
    {
        return String.Format("{0}",this.Value);
    }

    public static Radians Convert(object value)
    {
        if (value is Radians)
            return (Radians)value;
        if (value is Degrees)
            return (Degrees)value;

        return System.Convert.ToDouble(value);
    }
}

学位

public struct Degrees
{
    public double Value;       

    public Degrees(double value) { this.Value = value; }

    public Radians GetRadians()
    {
        return this;
    }

    public Degrees Reduce()
    {
        return this.GetRadians().Reduce();
    }

    public double Cos
    {
        get
        {
            return System.Math.Cos(this.GetRadians());
        }
    }

    public double Sin
    {
        get
        {
            return System.Math.Sin(this.GetRadians());
        }
    }

    #region operator overloading

    public static implicit operator Degrees(Radians rad)
    {
        return new Degrees(rad.Value * 180 / System.Math.PI);
    }

    public static implicit operator Degrees(int i)
    {
        return new Degrees((double)i);
    }

    public static implicit operator Degrees(float f)
    {
        return new Degrees((double)f);
    }

    public static implicit operator Degrees(double d)
    {
        return new Degrees(d);
    }

    public static implicit operator double(Degrees deg)
    {
        return deg.Value;
    }

    public static Degrees operator +(Degrees deg,int i)
    {
        return new Degrees(deg.Value + i);
    }

    public static Degrees operator +(Degrees deg,double dbl)
    {
        return new Degrees(deg.Value + dbl);
    }

    public static Degrees operator +(Degrees deg1,Degrees deg2)
    {
        return new Degrees(deg1.Value + deg2.Value);
    }

    public static Degrees operator +(Degrees deg,Radians rad)
    {
        return new Degrees(deg.Value + rad.GetDegrees().Value);
    }

    public static Degrees operator -(Degrees deg)
    {
        return new Degrees(-deg.Value);
    }

    public static Degrees operator -(Degrees deg,int i)
    {
        return new Degrees(deg.Value - i);
    }

    public static Degrees operator -(Degrees deg,double dbl)
    {
        return new Degrees(deg.Value - dbl);
    }

    public static Degrees operator -(Degrees deg1,Degrees deg2)
    {
        return new Degrees(deg1.Value - deg2.Value);
    }

    public static Degrees operator -(Degrees deg,Radians rad)
    {
        return new Degrees(deg.Value - rad.GetDegrees().Value);
    }

    #endregion

    public override string ToString()
    {
        return String.Format("{0}",this.Value);
    }

    public static Degrees Convert(object value)
    {
        if (value is Degrees)
            return (Degrees)value;
        if (value is Radians)
            return (Radians)value;

        return System.Convert.ToDouble(value);
    }
}

一些示例使用

当使用API​​时,这些真正有益.而在内部,您的组织可能决定严格遵守度数或弧度以避免混淆,至少在这些类中,您可以使用最有意义的类型.例如,公开消费的API或GUI API可以使用Degrees,而您的重的数学/触发或内部使用可能会使用Radians.考虑以下类/打印功能

public class MyRadiansShape
{
    public Radians Rotation { get; set; }
}

public class MyDegreesShape
{
    public Degrees Rotation { get; set; }
}

public static void PrintRotation(Degrees degrees,Radians radians)
{
    Console.WriteLine(String.Format("Degrees: {0},Radians: {1}",degrees.Value,radians.Value));
}

是的,代码很漂亮(非常模糊),但没关系!只是为了展示如何帮助减少意外混合.

var radiansShape = new MyRadiansShape() { Rotation = Math.PI / 2}; //prefer "Radians.HALF_PI" instead,but just as an example
var degreesShape = new MyDegreesShape() { Rotation = 90 };

PrintRotation(radiansShape.Rotation,radiansShape.Rotation);
PrintRotation(degreesShape.Rotation,degreesShape.Rotation);
PrintRotation(radiansShape.Rotation + degreesShape.Rotation,radiansShape.Rotation + degreesShape.Rotation);

//Degrees: 90,Radians: 1.5707963267949
//Degrees: 90,Radians: 1.5707963267949
//Degrees: 180,Radians: 3.14159265358979

那么它们对于实现基于角度的其他数学概念非常有用,例如极坐标:

double distance = 5;
Polar polarCoordinate = new Polar(distance,(degreesShape.Rotation - radiansShape.Rotation) + Radians.QUARTER_PI);
Console.WriteLine("Polar Coordinate Angle: " + (Degrees)polarCoordinate.Angle); //because it's easier to read degrees!
//Polar Coordinate Angle: 45

最后,您可以实现一个Point2D类(或使用System.Windows.Point)与/从Polar的隐式转换:

Point2D cartesianCoordinate = polarCoordinate;
Console.WriteLine(cartesianCoordinate.X + "," + cartesianCoordinate.Y);
//3.53553390593274,3.53553390593274

正如我所说,我想在这些类中再次传递,可能消除对Radians的双重隐式转换,以避免几个角色混合和可能的编译器模糊.在我们创建静态ONE_PI,HALF_PI(等等)字段之前,实际上已经有了这些,我们正在从Math.PI的一些倍数转换.

编辑:这是Polar类作为额外的隐式转换的演示.它利用了Radians类(因此它的隐式转换)和它的帮助方法和Point2D类.我没有把它包括在这里,但是Polar类可以轻松地实现与Point2D类交互的操作,但这些与这个讨论无关.

public struct Polar
{
    public double Radius;
    public Radians Angle;

    public double X { get { return Radius * Angle.Cos; } }
    public double Y { get { return Radius * Angle.Sin; } }

    public Polar(double radius,Radians angle)
    {
        this.Radius = radius;
        this.Angle = angle;
    }

    public Polar(Point2D point)
        : this(point.Magnitude(),point.GetAngleFromOrigin())
    {
    }

    public Polar(Point2D point,double radius)
        : this(radius,Point2D origin)
        : this(point - origin)
    {
    }

    public Point2D ToCartesian()
    {
        return new Point2D(X,Y);
    }

    public static implicit operator Point2D(Polar polar)
    {
        return polar.ToCartesian();
    }

    public static implicit operator Polar(Point2D vector)
    {
        return new Polar(vector);
    }
}

猜你在找的C#相关文章