आपके पास अच्छी शुरुआत है, लेकिन जैसे जॉन ने कहा, यह वर्तमान में टाइप-सुरक्षित नहीं है; कनवर्टर को यह सुनिश्चित करने के लिए कोई त्रुटि-जांच नहीं है कि यह दशमलव सेल्सियस मान है।
तो, इसे आगे बढ़ाने के लिए, मैं संरचनात्मक प्रकारों को शुरू करना शुरू करूंगा जो संख्यात्मक मान लेते हैं और इसे माप की इकाई पर लागू करते हैं। एंटरप्राइज़ आर्किटेक्चर के पैटर्न (उर्फ चार डिजाइन पैटर्न की गिरोह) में, इसे सबसे आम उपयोग के बाद "मनी" पैटर्न कहा जाता है, जो कि एक प्रकार की मुद्रा को दर्शाता है। पैटर्न किसी भी संख्यात्मक राशि के लिए रखता है जिसके लिए माप की इकाई को सार्थक होना आवश्यक है।
उदाहरण:
public enum TemperatureScale
{
Celsius,
Fahrenheit,
Kelvin
}
public struct Temperature
{
decimal Degrees {get; private set;}
TemperatureScale Scale {get; private set;}
public Temperature(decimal degrees, TemperatureScale scale)
{
Degrees = degrees;
Scale = scale;
}
public Temperature(Temperature toCopy)
{
Degrees = toCopy.Degrees;
Scale = toCopy.Scale;
}
}
अब, आप एक सरल प्रकार है कि आप एक परिणाम के रूप में जाने जाते तापमान लौट लागू करने के लिए है कि रूपांतरण आप कर रहे हैं एक तापमान उचित पैमाने की है कि लेने के लिए उपयोग कर सकते हैं, और राशि दूसरे पैमाने पर।
इनपुट के मिलान से जांचने के लिए आपके Funcs को एक अतिरिक्त पंक्ति की आवश्यकता होगी; आप lambdas उपयोग करने के लिए जारी रख सकते हैं, या आप इसे एक सरल रणनीति पैटर्न के साथ एक कदम आगे ले जा सकते हैं:
public interface ITemperatureConverter
{
public Temperature Convert(Temperature input);
}
public class FahrenheitToCelsius:ITemperatureConverter
{
public Temperature Convert(Temperature input)
{
if (input.Scale != TemperatureScale.Fahrenheit)
throw new ArgumentException("Input scale is not Fahrenheit");
return new Temperature(input.Degrees * 5m/9m - 32, TemperatureScale.Celsius);
}
}
//Implement other conversion methods as ITemperatureConverters
public class TemperatureConverter
{
public Dictionary<Tuple<TemperatureScale, TemperatureScale>, ITemperatureConverter> converters =
new Dictionary<Tuple<TemperatureScale, TemperatureScale>, ITemperatureConverter>
{
{Tuple.Create<TemperatureScale.Fahrenheit, TemperatureScale.Celcius>,
new FahrenheitToCelsius()},
{Tuple.Create<TemperatureScale.Celsius, TemperatureScale.Fahrenheit>,
new CelsiusToFahrenheit()},
...
}
public Temperature Convert(Temperature input, TemperatureScale toScale)
{
if(!converters.ContainsKey(Tuple.Create(input.Scale, toScale))
throw new InvalidOperationException("No converter available for this conversion");
return converters[Tuple.Create(input.Scale, toScale)].Convert(input);
}
}
क्योंकि रूपांतरण के इन प्रकार के दो तरह से कर रहे हैं, तो आप दोनों तरीकों को संभालने के लिए इंटरफ़ेस की स्थापना पर विचार कर सकते, "कन्वर्टबैक" विधि या इसी तरह के साथ जो सेल्सियस स्केल में तापमान लेगा और फारेनहाइट में परिवर्तित होगा। इससे आपकी कक्षा गिनती कम हो जाती है। फिर, कक्षा के उदाहरणों के बजाय, आपके शब्दकोश मूल्य कन्वर्टर्स के उदाहरणों पर विधियों के लिए पॉइंटर्स हो सकते हैं। यह मुख्य तापमान कनवर्टर रणनीति-पिकर को स्थापित करने की जटिलता को बढ़ाता है, लेकिन रूपांतरण रणनीति वर्गों की संख्या को कम करता है जिसे आपको परिभाषित करना होगा।
यह भी ध्यान दें कि त्रुटि-जांच रनटाइम पर की जाती है जब आप वास्तव में रूपांतरण करने की कोशिश कर रहे होते हैं, यह सुनिश्चित करने के लिए कि यह हमेशा सही है, इस कोड को सभी उपयोगों में पूरी तरह से परीक्षण किया जाना आवश्यक है। इससे बचने के लिए, आप सेल्सियस तापमान और फारेनहाइट तापमान तापमान का उत्पादन करने के लिए बेस तापमान वर्ग प्राप्त कर सकते हैं, जो कि उनके स्केल को निरंतर मूल्य के रूप में परिभाषित करेगा। फिर, ITEMperatureConverter को दो प्रकार के दोनों प्रकार के सामान्य बना दिया जा सकता है, दोनों तापमान, आपको संकलन-समय की जांच कर रहा है कि आप उस रूपांतरण को निर्दिष्ट कर रहे हैं जो आप सोचते हैं कि आप हैं। तापमान कनवर्टर को गतिशील रूप से ITemperatureConverters खोजने के लिए भी बनाया जा सकता है, वे किस प्रकार के रूप में परिवर्तित होंगे, और स्वचालित रूप से कन्वर्टर्स के शब्दकोश को सेट अप करने के लिए निर्धारित किए जा सकते हैं ताकि आपको नए जोड़ने के बारे में चिंता न करें। यह तापमान-आधारित वर्ग गणना में वृद्धि की लागत पर आता है; आपको एक के बजाय चार डोमेन कक्षाएं (आधार और तीन व्युत्पन्न कक्षाएं) की आवश्यकता होगी। यह तापमान कनवर्टर क्लास के निर्माण को भी धीमा कर देगा क्योंकि कनवर्टर डिक्शनरी को प्रतिबिंबित करने के लिए कोड काफी प्रतिबिंब का उपयोग करेगा।
आप "मार्कर कक्षाएं" बनने के लिए माप की इकाइयों के लिए enums भी बदल सकते हैं; खाली वर्ग जिनके अलावा कोई अर्थ नहीं है, वे उस वर्ग के हैं और अन्य वर्गों से प्राप्त होते हैं। इसके बाद आप "यूनिटऑफमेजर" कक्षाओं का एक पूर्ण पदानुक्रम परिभाषित कर सकते हैं जो माप की विभिन्न इकाइयों का प्रतिनिधित्व करते हैं, और सामान्य प्रकार के तर्क और बाधाओं के रूप में उपयोग किए जा सकते हैं; ITemperatureConverter दो प्रकार के लिए जेनेरिक हो सकता है, जिनमें से दोनों तापमान स्केल कक्षाओं के लिए बाध्य हैं, और सेल्सियस फारेनहाइट कनवर्टर कार्यान्वयन जेनेरिक इंटरफेस को सेल्सियस डिग्री और फारेनहाइट डेग्री दोनों को तापमान स्केल से व्युत्पन्न कर देगा। इससे आपको माप की इकाइयों को एक रूपांतरण की बाधाओं के रूप में प्रकट करने की अनुमति मिलती है, बदले में माप की इकाइयों के प्रकार के बीच रूपांतरण की अनुमति मिलती है (कुछ सामग्रियों की कुछ इकाइयों को रूपांतरण ज्ञात होते हैं; 1 ब्रिटिश शाही पिंट का वजन 1.25 पाउंड होता है)।
ये सभी डिज़ाइन निर्णय हैं जो इस डिज़ाइन में एक प्रकार के परिवर्तन को सरल बना देंगे, लेकिन कुछ लागत पर (या तो कुछ और करने के लिए कठिन या एल्गोरिदम प्रदर्शन कम करना)। यह क्या, आप के लिए वास्तव में "आसान" है समग्र आवेदन के संदर्भ और कोडिंग पर्यावरण, जिसमें आप काम में फैसला करने के लिए आप पर निर्भर है
संपादित करें:। उपयोग आप अपने संपादन से चाहते हैं,, तापमान के लिए बहुत आसान है । हालांकि, यदि आप एक सामान्य यूनिट कनवर्टर चाहते हैं जो किसी भी यूनिटफॉर्मर के साथ काम कर सके, तो अब आप एंट्स को माप की अपनी इकाइयों का प्रतिनिधित्व नहीं करना चाहते हैं, क्योंकि एम्स के पास कस्टम विरासत पदानुक्रम नहीं हो सकता है (वे सीधे System.Enum से प्राप्त होते हैं)।
आप निर्दिष्ट कर सकते हैं कि डिफ़ॉल्ट कन्स्ट्रक्टर किसी भी एनम को स्वीकार कर सकता है, लेकिन फिर आपको यह सुनिश्चित करना होगा कि एनम एक प्रकार की इकाई है जो माप की इकाई है, अन्यथा आप डायलॉग रेसल्ट वैल्यू में पास हो सकते हैं और कनवर्टर फ्रीक रनटाइम पर बाहर।
इसके बजाय, यदि आप एक यूनिट कनवर्टर चाहते हैं जो माप के अन्य इकाइयों के लिए किसी भी यूनिटऑफमेजर को लैम्बडास में परिवर्तित कर सकता है, तो मैं माप की इकाइयों को "मार्कर क्लास" के रूप में निर्दिष्ट करता हूं; छोटे स्टेटलेस "टोकन" है कि केवल में अर्थ है कि वे अपने स्वयं के प्रकार के होते हैं और उनके माता पिता से निकाले जाते हैं:
//The only functionality any UnitOfMeasure needs is to be semantically equatable
//with any other reference to the same type.
public abstract class UnitOfMeasure:IEquatable<UnitOfMeasure>
{
public override bool Equals(UnitOfMeasure other)
{
return this.ReferenceEquals(other)
|| this.GetType().Name == other.GetType().Name;
}
public override bool Equals(Object other)
{
return other is UnitOfMeasure && this.Equals(other as UnitOfMeasure);
}
public override operator ==(Object other) {return this.Equals(other);}
public override operator !=(Object other) {return this.Equals(other) == false;}
}
public abstract class Temperature:UnitOfMeasure {
public static CelsiusTemperature Celsius {get{return new CelsiusTemperature();}}
public static FahrenheitTemperature Fahrenheit {get{return new CelsiusTemperature();}}
public static KelvinTemperature Kelvin {get{return new CelsiusTemperature();}}
}
public class CelsiusTemperature:Temperature{}
public class FahrenheitTemperature :Temperature{}
public class KelvinTemperature :Temperature{}
...
public class UnitConverter
{
public UnitOfMeasure BaseUnit {get; private set;}
public UnitConverter(UnitOfMeasure baseUnit) {BaseUnit = baseUnit;}
private readonly Dictionary<UnitOfMeasure, Func<decimal, decimal>> converters
= new Dictionary<UnitOfMeasure, Func<decimal, decimal>>();
public void AddConverter(UnitOfMeasure measure, Func<decimal, decimal> conversion)
{ converters.Add(measure, conversion); }
public void Convert(UnitOfMeasure measure, decimal input)
{ return converters[measure](input); }
}
आप त्रुटि-जांच में डाल सकते हैं (जाँच करें कि इनपुट यूनिट एक रूपांतरण निर्दिष्ट है, जाँच एक है कि रूपांतरण जोड़ा जा रहा है एक यूओएम के लिए मूल प्रकार के रूप में एक ही माता पिता के साथ, आदि) जैसा कि आप फिट देखते हैं। आप यूनिट कनवर्टर को तापमान कनवर्टर बनाने के लिए भी प्राप्त कर सकते हैं, जिससे आप स्थिर, संकलन-समय प्रकार के चेक जोड़ सकते हैं और रन-टाइम चेक से बचने की अनुमति दे सकते हैं जो यूनिट कनवर्टर का उपयोग करना होगा।
मापक इकाई एफ # में समर्थन कर रहे हैं और मैं सी # के लिए यह एक अच्छा [सुविधा] हो सकता है लगता है (https://github.com/dotnet/roslyn/issues/144) vNext। अभी के लिए मुझे यह प्रोजेक्ट [क्वांटिटी टाइप्स] (https://github.com/objorke/QuantityTypes) मिला है जो सी # में माप की इकाइयों को लागू करता है। – orad