C# Int32在C中的扩展方法#
我设想能够编写流畅的代码,为代码库中的数字增加意义。假设您想要一个数字来表示以英里为单位的距离。你会有类似于:C# Int32在C中的扩展方法#,c#,.net,extension-methods,struct,C#,.net,Extension Methods,Struct,我设想能够编写流畅的代码,为代码库中的数字增加意义。假设您想要一个数字来表示以英里为单位的距离。你会有类似于: public class Distance { private double _distanceValue; private UnitOfMeasure _uom; public double DistanceValue { get { return _distanceValue; } set { _distanceValue
public class Distance {
private double _distanceValue;
private UnitOfMeasure _uom;
public double DistanceValue {
get { return _distanceValue; }
set { _distanceValue = value; }
}
public UnitOfMeasure Uom {
get { return _uom; }
set { _uom = value; }
}
}
public enum UnitOfMeasure {
Kilometers,
Miles,
Feet,
Inches,
Parsecs
}
用法:
var result = myMethod(100.Miles());
static class IntExtensions
{
public static Miles(this int i) { get { return new Miles { Count = i }; } }
}
public struct Miles
{
public int Count { get; private set; } //optionally perform bounds checking
}
我认为这将比简单地传递int更具可读性,而且您可能会对Miles类型应用边界检查
扩展方法和结构实现:
var result = myMethod(100.Miles());
static class IntExtensions
{
public static Miles(this int i) { get { return new Miles { Count = i }; } }
}
public struct Miles
{
public int Count { get; private set; } //optionally perform bounds checking
}
这样的想法有用吗,还是在一个炎热的星期五太迟了
编辑:是的,如果没有扩展属性,看起来就不那么整洁了。。。为无效代码道歉。这只是一个想法。您的
Miles
结构应该是不可变的
换成
public struct Miles {
public Miles(int count) : this() { Count = count; } //optionally perform bounds checking
public int Count { get; private set; }
}
一条评论:让
Miles
可变有什么意义?一个int
是不可变的,为什么一旦它有了一个单位,就让它可变呢
(另外,在C#4中引入扩展属性?否则这将不起作用。)
最后,如果您想添加单元,它们应该是可组合的,我目前不知道如何实现这一点
例如,应编译以下代码:
var x = 100.km;
var y = 10.sec;
var kmh = x / y; // What type does kmh have?
< C++ >中有一个库,它通过将类型表示为所有七个基本物理单元的维度元组来实现这一点,但是这在C语言中不起作用,因为它需要整数作为模板参数。 < P>个人,我没有看到一点。 我看不出myMethod的签名不应该是:
public object MyMethod(int miles)
{
// bounds checking on int here
// then logic
}
您还可以使用代码契约使事情更加明确
添加对.Miles()的调用并使int变为易变更令人困惑。以下是您的设计 请注意,我们在C中没有扩展属性,但它只是扩展方法。
class Program
{
static void Main(string[] args)
{
var result = myMethod(100.ToMiles());
//Miles miles = 100.ToMiles();
}
}
static class IntExtensions
{
public static Miles ToMiles(this int miles)
{
return new Miles(miles);
}
}
struct Miles
{
public int Count { get; private set; }
public Miles(int count)
: this()
{
if (count < 0)
{
throw new ArgumentException("miles type cannot hold negative values.");
}
this.Count = count;
}
}
类程序
{
静态void Main(字符串[]参数)
{
var result=myMethod(100.ToMiles());
//英里=100.t英里();
}
}
静态类扩展
{
公共静态英里数(此整数英里数)
{
返回新里程(英里);
}
}
结构英里数
{
公共整数计数{get;私有集;}
公共英里数(整数计数)
:此()
{
如果(计数<0)
{
抛出新ArgumentException(“miles类型不能包含负值”);
}
这个.Count=Count;
}
}
您的代码中不应该包含扩展方法,编写扩展方法并不能缓解问题。你仍然有一个神奇的数字
如果它是常量,则将其设为常量,并在常量名称中包含u英里
另外,为什么不将该值封装在名为Distance的类或结构中,该类或结构只包含一个数值和一个指定度量单位的枚举
比如:
public class Distance {
private double _distanceValue;
private UnitOfMeasure _uom;
public double DistanceValue {
get { return _distanceValue; }
set { _distanceValue = value; }
}
public UnitOfMeasure Uom {
get { return _uom; }
set { _uom = value; }
}
}
public enum UnitOfMeasure {
Kilometers,
Miles,
Feet,
Inches,
Parsecs
}
我用这个想法为一个项目创建了一种内部语法,它处理物理度量。起初我对这种方法持怀疑态度,但现在我真的很喜欢它,因为它使源代码非常可读,编写起来也很简单有趣。下面是一个例子: 单位类型:
public struct Celsius : IEquatable<Celsius>
{
private readonly Double _value;
public const string Abbreviation = "°C";
public Celsius(Double value)
{
_value = value;
}
public Boolean Equals(Celsius other)
{
return _value == other._value;
}
public override Boolean Equals(Object other)
{
if (!(other is Celsius))
{
return false;
}
return Equals((Celsius)other);
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public override string ToString()
{
return _value + Abbreviation;
}
public static explicit operator Celsius(Double value)
{
return new Celsius(value);
}
public static explicit operator Double(Celsius value)
{
return value._value;
}
public static Boolean operator >(Celsius l, Celsius r)
{
return l._value > r._value;
}
public static bool operator <(Celsius l, Celsius r)
{
return l._value < r._value;
}
public static Boolean operator >=(Celsius l, Celsius r)
{
return l._value >= r._value;
}
public static bool operator <=(Celsius l, Celsius r)
{
return l._value <= r._value;
}
public static Boolean operator ==(Celsius l, Celsius r)
{
return l._value == r._value;
}
public static bool operator !=(Celsius l, Celsius r)
{
return l._value != r._value;
}
}
public static class UnitsExtensions
{
public static Celsius Celsius(this Double value)
{
return new Celsius(value);
}
public static Celsius Celsius(this Single value)
{
return new Celsius(value);
}
public static Celsius Celsius(this Int32 value)
{
return new Celsius(value);
}
public static Celsius Celsius(this Decimal value)
{
return new Celsius((Double)value);
}
public static Celsius? Celsius(this Decimal? value)
{
return value == null ? default(Celsius?) : new Celsius((Double)value);
}
}
var temp = (Celsius)value;
if (temp <= 0.Celsius())
{
Console.Writeline("It's cold!");
}
else if (temp < 20.Celsius())
{
Console.Writeline("Chilly...");
}
else if (temp < 30.Celsius())
{
Console.Writeline("It's quite lovely");
}
else
{
Console.Writeline("It's hot!");
}
用法:
public struct Celsius : IEquatable<Celsius>
{
private readonly Double _value;
public const string Abbreviation = "°C";
public Celsius(Double value)
{
_value = value;
}
public Boolean Equals(Celsius other)
{
return _value == other._value;
}
public override Boolean Equals(Object other)
{
if (!(other is Celsius))
{
return false;
}
return Equals((Celsius)other);
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public override string ToString()
{
return _value + Abbreviation;
}
public static explicit operator Celsius(Double value)
{
return new Celsius(value);
}
public static explicit operator Double(Celsius value)
{
return value._value;
}
public static Boolean operator >(Celsius l, Celsius r)
{
return l._value > r._value;
}
public static bool operator <(Celsius l, Celsius r)
{
return l._value < r._value;
}
public static Boolean operator >=(Celsius l, Celsius r)
{
return l._value >= r._value;
}
public static bool operator <=(Celsius l, Celsius r)
{
return l._value <= r._value;
}
public static Boolean operator ==(Celsius l, Celsius r)
{
return l._value == r._value;
}
public static bool operator !=(Celsius l, Celsius r)
{
return l._value != r._value;
}
}
public static class UnitsExtensions
{
public static Celsius Celsius(this Double value)
{
return new Celsius(value);
}
public static Celsius Celsius(this Single value)
{
return new Celsius(value);
}
public static Celsius Celsius(this Int32 value)
{
return new Celsius(value);
}
public static Celsius Celsius(this Decimal value)
{
return new Celsius((Double)value);
}
public static Celsius? Celsius(this Decimal? value)
{
return value == null ? default(Celsius?) : new Celsius((Double)value);
}
}
var temp = (Celsius)value;
if (temp <= 0.Celsius())
{
Console.Writeline("It's cold!");
}
else if (temp < 20.Celsius())
{
Console.Writeline("Chilly...");
}
else if (temp < 30.Celsius())
{
Console.Writeline("It's quite lovely");
}
else
{
Console.Writeline("It's hot!");
}
var-temp=(摄氏度)值;
如果(temp这是一个有趣的想法,但在做类似的事情之前,我会要求一个非常强大的用例。首先,一旦一个数字被转换为“英里”,您不能再将其视为int。您要么必须实现整个运算符范围,要么在对其执行算术运算之前将英里数转换回整数。如果没有充分的理由这样做,这将是大量额外的工作
不过,在某些情况下,这将是一个很好的策略。我想我记得有一次听说过一枚价值数百万美元的火箭或其他东西失败了,因为程序员将错误的度量单位传递到了一个函数中。这将帮助你避免这个问题
另一方面,你可能会对F#感兴趣,因为它有。我从前面的SO问题中抓住了这个(稍作调整)。我更喜欢这种风格,因为它符合DateTime和TimeSpan等常用方法
[StructLayout(LayoutKind.Sequential), ComVisible(true)]
public struct Distance : IEquatable<Distance>, IComparable<Distance>
{
private const double MetersPerKilometer = 1000.0;
private const double CentimetersPerMeter = 100.0;
private const double CentimetersPerInch = 2.54;
private const double InchesPerFoot = 12.0;
private const double FeetPerYard = 3.0;
private const double FeetPerMile = 5280.0;
private const double FeetPerMeter = CentimetersPerMeter / (CentimetersPerInch * InchesPerFoot);
private const double InchesPerMeter = CentimetersPerMeter / CentimetersPerInch;
public static readonly Distance Zero = new Distance(0.0);
private readonly double meters;
/// <summary>
/// Initializes a new Distance to the specified number of meters.
/// </summary>
/// <param name="meters"></param>
public Distance(double meters)
{
this.meters = meters;
}
/// <summary>
/// Gets the value of the current Distance structure expressed in whole and fractional kilometers.
/// </summary>
public double TotalKilometers
{
get
{
return meters / MetersPerKilometer;
}
}
/// <summary>
/// Gets the value of the current Distance structure expressed in whole and fractional meters.
/// </summary>
public double TotalMeters
{
get
{
return meters;
}
}
/// <summary>
/// Gets the value of the current Distance structure expressed in whole and fractional centimeters.
/// </summary>
public double TotalCentimeters
{
get
{
return meters * CentimetersPerMeter;
}
}
/// <summary>
/// Gets the value of the current Distance structure expressed in whole and fractional yards.
/// </summary>
public double TotalYards
{
get
{
return meters * FeetPerMeter / FeetPerYard;
}
}
/// <summary>
/// Gets the value of the current Distance structure expressed in whole and fractional feet.
/// </summary>
public double TotalFeet
{
get
{
return meters * FeetPerMeter;
}
}
/// <summary>
/// Gets the value of the current Distance structure expressed in whole and fractional inches.
/// </summary>
public double TotalInches
{
get
{
return meters * InchesPerMeter;
}
}
/// <summary>
/// Gets the value of the current Distance structure expressed in whole and fractional miles.
/// </summary>
public double TotalMiles
{
get
{
return meters * FeetPerMeter / FeetPerMile;
}
}
/// <summary>
/// Returns a Distance that represents a specified number of kilometers.
/// </summary>
/// <param name="value">A number of kilometers.</param>
/// <returns></returns>
public static Distance FromKilometers(double value)
{
return new Distance(value * MetersPerKilometer);
}
/// <summary>
/// Returns a Distance that represents a specified number of meters.
/// </summary>
/// <param name="value">A number of meters.</param>
/// <returns></returns>
public static Distance FromMeters(double value)
{
return new Distance(value);
}
/// <summary>
/// Returns a Distance that represents a specified number of centimeters.
/// </summary>
/// <param name="value">A number of centimeters.</param>
/// <returns></returns>
public static Distance FromCentimeters(double value)
{
return new Distance(value / CentimetersPerMeter);
}
/// <summary>
/// Returns a Distance that represents a specified number of yards.
/// </summary>
/// <param name="value">A number of yards.</param>
/// <returns></returns>
public static Distance FromYards(double value)
{
return new Distance(value * FeetPerYard / FeetPerMeter);
}
/// <summary>
/// Returns a Distance that represents a specified number of feet.
/// </summary>
/// <param name="value">A number of feet.</param>
/// <returns></returns>
public static Distance FromFeet(double value)
{
return new Distance(value / FeetPerMeter);
}
/// <summary>
/// Returns a Distance that represents a specified number of inches.
/// </summary>
/// <param name="value">A number of inches.</param>
/// <returns></returns>
public static Distance FromInches(double value)
{
return new Distance(value / InchesPerMeter);
}
/// <summary>
/// Returns a Distance that represents a specified number of miles.
/// </summary>
/// <param name="value">A number of miles.</param>
/// <returns></returns>
public static Distance FromMiles(double value)
{
return new Distance(value * FeetPerMile / FeetPerMeter);
}
public static bool operator ==(Distance a, Distance b)
{
return (a.meters == b.meters);
}
public static bool operator !=(Distance a, Distance b)
{
return (a.meters != b.meters);
}
public static bool operator >(Distance a, Distance b)
{
return (a.meters > b.meters);
}
public static bool operator >=(Distance a, Distance b)
{
return (a.meters >= b.meters);
}
public static bool operator <(Distance a, Distance b)
{
return (a.meters < b.meters);
}
public static bool operator <=(Distance a, Distance b)
{
return (a.meters <= b.meters);
}
public static Distance operator +(Distance a, Distance b)
{
return new Distance(a.meters + b.meters);
}
public static Distance operator -(Distance a, Distance b)
{
return new Distance(a.meters - b.meters);
}
public static Distance operator -(Distance a)
{
return new Distance(-a.meters);
}
public override bool Equals(object obj)
{
if (!(obj is Distance))
return false;
return Equals((Distance)obj);
}
public bool Equals(Distance value)
{
return this.meters == value.meters;
}
public int CompareTo(Distance value)
{
return this.meters.CompareTo(value.meters);
}
public override int GetHashCode()
{
return meters.GetHashCode();
}
public override string ToString()
{
return string.Format("{0} meters", TotalMeters);
}
}
[StructLayout(LayoutKind.Sequential),ComVisible(true)]
公共结构距离:I可比,I可比
{
私人常数双表Perkilometer=1000.0;
私人常数双厘米测光计=100.0;
私人康斯特双厘米斯普林奇=2.54;
私有常量双英寸ESPERFOOT=12.0;
private const double FeetPerYard=3.0;
私人常数双FeetPerMile=5280.0;
私人恒量双测力计=厘米测力计/(厘米测力*英寸测力);
专用常数双英寸塞培计=厘米塞培计/厘米塞培计;
公共静态只读距离0=新距离(0.0);
私人只读双表;
///
///将新距离初始化为指定的米数。
///
///
公共距离(双米)
{
这个。米=米;
}
///
///获取当前距离结构的值,以整公里和分数公里表示。
///
公共双里程
{
得到
{
返回仪表/仪表/飞行仪表;
}
}
///
///获取当前距离结构的值,以整米和分数米表示。
///
公共双积算仪
{
得到
{
返回仪表;
}
}
///
///获取当前距离结构的值,以整数和分数厘米表示。
///
公共双总厘米
{
得到
{
返回米*厘米米;
}
}
///
///获取当前距离的值