我已经写了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 /摄氏度
该模式似乎是:隐式转换大多数(只有?)在定义数值/值类型时有用,转换由公式定义.回想起来,这是很明显的.不过,我想知道非数字类是否也可以受益于隐式转换?
解决方法
我拉出了我们目前正在使用的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); } }