2010-10-22 46 views
10

我有很多計算項目涉及到很多現實世界單位:使用現實世界的單位,而不是類型

  • 距離;
  • 溫度;
  • 流速;
  • ...

該項目涉及複雜和衆多的計算公式。

這就是爲什麼我應該像溫度距離使用自定義類型的...可以很好的爲代碼的可讀性。例如:

Temperature x = -55.3; 
Meter y = 3; 

var x = new Temperature(-55.3); 

我試圖使一個使用雙內值的溫度等級。

public class Temperature 
{ 
    double _Value = double.NaN; 

    public Temperature() { } 

    public Temperature(double v) { 
     _Value = v; 
    } 

    public static implicit operator Temperature(double v) { 
     return new Temperature(v); 
    } 
} 

但是班級可以爲空。這意味着類似於:

Temperature myTemp; 

是「正確的」並且將爲空。我不想要這個。我不想使用結構,因爲他們太有限:

  • 他們不能使用無參數的構造函數,也沒有實例字段intializers像double _Value = double.Nan;來定義默認值(我的魔杖默認標的雙重價值是NAN)
  • 他們不能從類繼承,他們纔可以實現接口

他們我不知道是否有辦法告訴C#:

Temperature myTemp = 23K; // C# does not implement anything to make K unit... 

但我知道C#不處理自定義單位。

Temperature myTemp = new Kelvin(23); // This might work 

所以,我想我可以創建一個從溫度遺傳了兩個攝氏度和Kelvin類,然後我開始懷疑,如果這個想法真的很值得的,因爲它涉及到很多編碼和測試。

這是討論中我想開始:

會在我的代碼,而不是.NET類型使用現實世界的單位將是好事還是壞事?有沒有人已經?什麼是陷阱和最佳實踐?或者我應該更好地遠離這一點,並使用標準的.NET類型?

+2

旁註:F#本地支持單元 – CodesInChaos 2010-10-22 10:28:03

+7

爲什麼你認爲結構是有限的?我認爲結構正是我在這裏使用的。你的類將只攜帶一個數字,不可變的值 - 對於結構來說是理想的用例。 – NOtherDev 2010-10-22 10:29:03

+0

Interresting!不幸的是,我不能在我的項目中使用F#。但我肯定會注意到這個將來 – Larry 2010-10-22 10:30:14

回答

1

實現此目的的一種方法是使用基本對象的組合(您的案例中爲Temperature)和專門用於基本對象的TemperatureTraits類。通過類比C++,String等價類basic_string實際上是一個類模板(C#術語中的泛型),它不僅包含字符串元素(char,寬字符)的模板參數,還包含一個traits類,詳細說明該類的行爲給定類型的字符串元素(例如char_traits)。

在你的情況,你可以定義一個通用的像

public class MeasurableWithUnits<class M MEASURABLE, class U UNITS>

,然後實施將不僅取決於可測量的類,但也對單位類。這在實踐中會有多大用處取決於多少這樣一個對象可以做成真正通用的 - 在MeasurableUnits的組合中哪些操作是共同的?

如果這種方法看起來很有趣,那麼有一篇關於C#特性here的研究論文。

+0

這是一個*真正* interresting紙。不幸的是,由於時間的限制,我不認爲我可以將它應用於這個項目,而且我必須關注計算的複雜性。無論如何+1和鏈接書籤。謝謝 ! – Larry 2010-10-22 16:37:47

+1

@controlbreak - 你見過這個相關的文章嗎? http://stackoverflow.com/questions/348853/units-of-measure-in-c-almost – 2010-10-22 16:50:20

+0

我愛最後一個鏈接。公認! – Larry 2010-10-23 18:50:55

0

我認爲當您想要將更多特定功能添加到溫度時(例如:IsFreezing())可能會更好。

要解決開爾文和攝氏問題:使接口ITemperature和基類。在基類中,您可以實現接口併爲所有類填寫相同的細節。

0

如果您使用結構,那麼這不能成爲null

struct Temperature 
{ 
    double _Value; 
} 
0

我不認爲這是值得添加靜態類型在C#機組。您需要重載如此多的操作員(對於所有單位組合,而不僅僅是所有單位)。並建立在像正常雙打Math.Sqrt工作,職能...

什麼,你可以嘗試使用動態類型:在調試模式下進行編譯時

class PhysicalUnit 
{ 
} 

struct PhysicalValue 
{ 
    readonly Value; 
    readonly PhysicalUnit; 
} 

,然後添加檢查單位結合在一起。在發行版中,只需刪除PhysicalUnit字段和所有檢查,並且(幾乎)與使用普通雙打的代碼一樣快。

10

爲什麼不嘗試一個結構,看起來像這樣:

/// <summary> 
/// Temperature class that uses a base unit of Celsius 
/// </summary> 
public struct Temp 
{ 
    public static Temp FromCelsius(double value) 
    { 
     return new Temp(value); 
    } 

    public static Temp FromFahrenheit(double value) 
    { 
     return new Temp((value - 32) * 5/9); 
    } 

    public static Temp FromKelvin(double value) 
    { 
     return new Temp(value - 273.15); 
    } 

    public static Temp operator +(Temp left, Temp right) 
    { 
     return Temp.FromCelsius(left.Celsius + right.Celsius); 
    } 

    private double _value; 

    private Temp(double value) 
    { 
     _value = value; 
    } 

    public double Kelvin 
    { 
     get { return _value + 273.15; } 
    } 

    public double Celsius 
    { 
     get { return _value; } 
    } 

    public double Fahrenheit 
    { 
     get { return _value/5 * 9 + 32; } 
    } 
} 

然後使用它像,說,這:

static void Main(string[] args) 
    { 
     var c = Temp.FromCelsius(30); 
     var f = Temp.FromFahrenheit(20); 
     var k = Temp.FromKelvin(20); 

     var total = c + f + k; 
     Console.WriteLine("Total temp is {0}F", total.Fahrenheit); 
    } 
+1

對於**不可變**結構樣本+1。 – 2010-10-22 10:58:38

+0

問題在於你需要大量的類和更多的重載操作符。由於在物理*許多單位的組合是有用的。 – CodesInChaos 2010-10-22 11:50:22

+0

但這只是一個溫度的想法。如果我們在語言中遇到了一些限制,那麼如何解決這個問題呢?我不認爲這是一個問題。無論如何,每個單元都必須發生超載。與問題中提出的更多類相比,此方法可最大限度地減少它們。 – 2010-10-22 20:28:10

0

我會讓Temperature存儲溫度抽象類(在開爾文!)在一個InternalTemperature屬性。

派生類Celcius會將輸入值轉化爲開爾文。它會有一個(只讀)Value值將內部值轉換回來。

比較它們(比另一個更暖和)會很容易。

相關問題