我应该何时在C#中定义(显式或隐式)转换运算符?
C#的一个鲜为人知的特性是可以创建隐式或显式的用户定义类型转换 。 我已经写了6年的C#代码了,我从来没有用过它。 所以,我担心我可能错过了好机会。
什么是用户定义转换的合法,良好用途? 您是否有比仅定义自定义方法更好的示例?
–
事实certificate,微软有一些关于转换的设计指南 ,其中最相关的是:
如果最终用户未明确预期此类转换,请勿提供转换运算符。
但什么时候转换“预期”? 在玩具编号课程之外,我无法弄清楚任何真实世界的用例。
以下是答案中提供的示例摘要:
模式似乎是:隐式转换大多数(仅?)在定义数值/值类型时很有用,转换由公式定义。 回想起来,这很明显。 不过,我想知道非数字类是否也可以从隐式转换中受益..?
正如评论中所提到的,度和旋转是避免混合双值的一个很好的例子,尤其是在API之间。
我拿出了我们目前正在使用的Radians
and Degrees
课程,他们在这里。 现在看看它们(经过这么长时间)我想要清理它们(特别是注释/文档)并确保它们经过适当的测试。 值得庆幸的是,我已经设法有时间安排这样做。 无论如何,使用这些都需要您自担风险,我不能保证这里的所有数学是否正确,因为我很确定我们没有实际使用/测试过我们写的所有function。
弧度
/// /// Defines an angle in Radians /// 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 /// /// Angle value /// public double Value; /// /// Finds the Cosine of the angle /// public double Cos { get { return System.Math.Cos(this); } } /// /// Finds the Sine of the angle /// public double Sin { get { return System.Math.Sin(this); } } #endregion /// /// Constructor /// /// angle value in radians public Radians(double value) { this.Value = value; } /// /// Gets the angle in degrees /// /// Returns the angle in degrees 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 /// /// Conversion of Degrees to Radians /// /// /// public static implicit operator Radians(Degrees deg) { return new Radians(deg.Value * System.Math.PI / 180); } /// /// Conversion of integer to Radians /// /// /// public static implicit operator Radians(int i) { return new Radians((double)i); } /// /// Conversion of float to Radians /// /// /// public static implicit operator Radians(float f) { return new Radians((double)f); } /// /// Conversion of double to Radians /// /// /// public static implicit operator Radians(double dbl) { return new Radians(dbl); } /// /// Conversion of Radians to double /// /// /// public static implicit operator double(Radians rad) { return rad.Value; } /// /// Add Radians and a double /// /// /// /// public static Radians operator +(Radians rad, double dbl) { return new Radians(rad.Value + dbl); } /// /// Add Radians to Radians /// /// /// /// public static Radians operator +(Radians rad1, Radians rad2) { return new Radians(rad1.Value + rad2.Value); } /// /// Add Radians and Degrees /// /// /// /// public static Radians operator +(Radians rad, Degrees deg) { return new Radians(rad.Value + deg.GetRadians().Value); } /// /// Sets Radians value negative /// /// /// public static Radians operator -(Radians rad) { return new Radians(-rad.Value); } /// /// Subtracts a double from Radians /// /// /// /// public static Radians operator -(Radians rad, double dbl) { return new Radians(rad.Value - dbl); } /// /// Subtracts Radians from Radians /// /// /// /// public static Radians operator -(Radians rad1, Radians rad2) { return new Radians(rad1.Value - rad2.Value); } /// /// Subtracts Degrees from Radians /// /// /// /// 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
。 考虑以下类/打印function:
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
的double
隐式转换,以避免可能的几个极端情况混淆和编译器歧义。 在我们创建静态ONE_PI
, HALF_PI
(等等)字段之前,我们实际上就是那里,我们从Math.PI
double的多个转换。
编辑:这是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, point.GetAngleFromOrigin()) { } public Polar(Point2D point, 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); } }
当与其他类型进行自然而明确的转换时,您可以使用转换运算符。
比如说你有一个表示温度的数据类型:
public enum TemperatureScale { Kelvin, Farenheit, Celsius } public struct Temperature { private TemperatureScale _scale; private double _temp; public Temperature(double temp, TemperatureScale scale) { _scale = scale; _temp = temp; } public static implicit operator Temperature(double temp) { return new Temperature(temp, TemperatureScale.Kelvin); } }
使用隐式运算符,您可以为温度变量赋值,它将自动用作开尔文:
Temperature a = new Temperature(100, TemperatureScale.Celcius); Temperature b = 373.15; // Kelvin is default
我使用它来从DateTime
到"yyyyMMdd"
或其对应的int
(yyyyMMdd)值进行无缝转换。
例如:
void f1(int yyyyMMdd); void f2(string yyyyMMdd); ... f1(30.YearsFrom(DateTime.Today)); f2(30.YearsFrom(DateTime.Today)); ... public static DateAsYyyyMmDd YearsFrom(this int y, DateTime d) { return new DateAsYyyyMmDd(d.AddYears(y)); } ... public class DateAsYyyyMmDd { private readonly DateTime date; public DateAsYyyyMmDd(DateTime date) { this.date = date; } public static implicit operator int(DateOrYyyyMmDd d) { return Convert.ToInt32(d.date.ToString("yyyyMMdd")); } public static implicit operator string(DateOrYyyyMmDd d) { return d.date.ToString("yyyyMMdd"); } }
假设您有一个用于商店应用程序的产品(例如玩具)的类:
class Product { string name; decimal price; string maker; //etc... }
您可以定义可能执行以下操作的显式强制转换:
public static explicit operator string(Product p) { return "Product Name: " + p.name + " Price: " + p.price.ToString("C") + " Maker: " + p.maker; // Or you might just want to return the name. }
这样你做的事情如下:
textBox1.Text = (string)myProduct;
它会将输出格式化为Product
类的显式运算符。
如果最终用户未明确预期此类转换,请勿提供转换运算符。
微软的意思是,如果您确实提供了转换运算符,则不会返回不期望的结果。 使用我们的Product
类的最后一个示例,这将返回一个不期望的结果:
public static explicit operator string(Product p) { return (p.price * 100).ToString(); //... }
显然没有人会这样做,但如果其他人使用Product
类并使用显式字符串转换,他们就不会期望它返回100的价格。
希望这可以帮助!
通常,如果两件事物在逻辑上可兑换。 我在这种情况下使用它们来提供更流畅的代码。 我有时也会使用它们来解决那些不像我期望的那样工作的语言function。
这是一个非常简单,人为的例子,它说明了最后一个与我在生产中使用的东西相似的想法……
上述就是C#学习教程:我应该何时在C#中定义(显式或隐式)转换运算符?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
class Program {. static void Main(string[] args) { Code code1 = new Code { Id = 1, Description = "Hi" }; Code code2 = new Code { Id = 2, Description = "There" }; switch (code1) { case 23: // do some stuff break; // other cases... } } } public class Code { private int id; private string description; public int Id { get; set; } public string Description { get; set; } public static implicit operator int(Code code) { return code.Id; } }
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/984954.html