簡體   English   中英

C#中Int32的擴展方法

[英]Extension method for Int32 in C#

我設想能夠編寫流暢的代碼,為代碼庫中的數字增加意義。 假設您想要一個數字來表示以英里為單位的距離。 你有類似的東西:

用法

var result = myMethod(100.Miles());

我認為這比簡單傳遞int更具可讀性,而且你可以將邊界檢查應用於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 _。

另外,為什么不將值包裝在名為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
}

這是一個有趣的想法,但在做這樣的事情之前我會要求一個非常強大的用例。 首先,一旦將數字轉換為“英里”,您就不能再將其視為int。 您要么必須實現整個運算符范圍,要么在對它們進行算術運算之前將數英里轉換回整數。 如果沒有充分的理由去做,那就是額外的工作。

但在某些情況下,這將是一個很好的策略。 我想我曾經記得聽說過數百萬美元的火箭或 曾經失去過1.25億美元太空飛船 失敗的 美國宇航局的 事情, 因為程序員正在將錯誤的測量單位傳遞給一個函數。 這可以幫助您避免這個問題。

在相關的說明中,您可能對F#感興趣,它對計量單位具有內置支持

你的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#中不起作用,因為它需要整數作為模板參數。

我用這個想法為一個處理物理測量的項目創建一種內部語法。 我一開始對這種方法表示懷疑,但我現在非常喜歡它,因為它使源代碼非常易讀且易於編寫。 這是一個例子:

單位類型:

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!");
}

我有很多這些類型的各種措施,像MillimeterRadiansDegreesMillimetersPerSecond ,等我甚至至今為實現分裂,這樣,當我把,說,走了MillimetersPerSecondMillimeters ,我得到一個TimeSpan值作為回報。 也許這是落伍的,但我發現類型安全和使用類型的心理容易值得努力實現和維護它們。

這就是你的設計應該如何。

請注意, 我們在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;
    }
}

我從之前的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);
        }
    }

就個人而言,我沒有看到一點。

我認為myMethod的簽名不應該是:

public object MyMethod(int miles)
{
    // bounds checking on int here
    // then logic 
}

您還可以使用代碼約定使事情更加明確。

添加對.Miles()的調用並使int Mutable更令人困惑。

public static class Int32Extensions
{
    public static Miles ToMiles( this Int32 distance )
    {
        return new Miles( distance );
    }
}

public class Miles
{
    private Int32 _distance;

    public Miles( Int32 distance )
    {
        _distance = distance;
    }

    public Int32 Distance
    {
        get
        {
            return _distance;
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM