2012-02-29 17 views
10

मैं विभिन्न प्रकार के वर्गों को संभालने के लिए कुछ बढ़ते if संरचनाओं को संभालने का बेहतर तरीका ढूंढने की कोशिश कर रहा हूं। ये कक्षाएं, आखिरकार, कुछ अतिरिक्त राज्य जानकारी के साथ अलग-अलग मूल्य प्रकारों (int, DateTime, आदि) के आसपास रैपर हैं। तो इन वर्गों के बीच प्राथमिक अंतर उनके पास मौजूद डेटा का प्रकार है। जबकि वे जेनेरिक इंटरफेस को लागू करते हैं, उन्हें भी सजातीय संग्रह में रखा जाना चाहिए, इसलिए वे एक गैर-सामान्य इंटरफेस भी लागू करते हैं। कक्षा के उदाहरणों को उनके द्वारा प्रस्तुत किए जाने वाले डेटा के प्रकार के अनुसार संभाला जाता है और उनका प्रचार जारी रहता है या उस पर आधारित नहीं रहता है।डबल-डिस्पैच और विकल्प

हालांकि यह आवश्यक नहीं है .NET या C# समस्या, मेरा कोड सी # में है।

उदाहरण कक्षाएं: बस को संभालने के लिए

डबल डिस्पैच

मैं हाल ही में आगंतुक पैटर्न का पता चला और डबल प्रेषण के अपने प्रयोग:

interface ITimedValue { 
TimeSpan TimeStamp { get; } 
} 

interface ITimedValue<T> : ITimedValue { 
T Value { get; } 
} 

class NumericValue : ITimedValue<float> { 
public TimeSpan TimeStamp { get; private set; } 
public float Value { get; private set; } 
} 

class DateTimeValue : ITimedValue<DateTime> { 
public TimeSpan TimeStamp { get; private set; } 
public DateTime Value { get; private set; } 
} 

class NumericEvaluator { 
public void Evaluate(IEnumerable<ITimedValue> values) ... 
} 

मैं दो विकल्प के साथ आए हैं ऐसा मामला यह अपील करता है क्योंकि यह अवांछित डेटा को प्रचारित करने की अनुमति नहीं देगा (अगर हम केवल एक int को संभालना चाहते हैं, तो हम डेटटाइम से अलग तरीके से इसे संभाल सकते हैं)। साथ ही, विभिन्न प्रकारों को कैसे संभाला जाता है, इस व्यवहार के व्यवहार को प्रेषण को संभालने वाली एकल कक्षा तक सीमित रखा जाएगा। लेकिन अगर एक नया मूल्य प्रकार समर्थित होना चाहिए तो रखरखाव का एक उचित हिस्सा है।

संघ कक्षा

एक वर्ग जो समर्थित हो सकता है प्रत्येक मान प्रकार के लिए एक संपत्ति में शामिल है क्या इन कक्षाओं की दुकान में से प्रत्येक। किसी मूल्य पर कोई भी ऑपरेशन उचित घटक को प्रभावित करेगा। यह डबल-प्रेषण रणनीति की तुलना में कम जटिल और कम रखरखाव है, लेकिन इसका मतलब यह होगा कि डेटा का हर टुकड़ा अनावश्यक रूप से सभी तरह से प्रचार करेगा क्योंकि आप अब उस डेटा प्रकार पर काम नहीं करते हैं, "। हालांकि, अगर नए प्रकारों को समर्थित करने की आवश्यकता है, तो उन्हें केवल इस कक्षा में जाना होगा (साथ ही जो भी अतिरिक्त वर्ग जो नए डेटा प्रकार का समर्थन करने के लिए बनाए जाने की आवश्यकता है)।

class UnionData { 
public int NumericValue; 
public DateTime DateTimeValue; 
} 

क्या बेहतर विकल्प हैं? क्या इन दो विकल्पों में से कुछ में कुछ है जो मैंने नहीं सोचा था कि मुझे चाहिए?

+0

संभवतः 'न्यूमेरिक एवल्यूलेटर' की 'मूल्यांकन' विधि में क्या हो सकता है जो 'डेटटाइम' * या * फ्लोट पर काम करेगा? –

+0

अभी मोबाइल पर, इसलिए मैं एक उचित उत्तर नहीं लिख सकता, लेकिन डबल प्रेषण के लिए गतिशील उपयोग के लिए गुगल करने का प्रयास करें (जो विज़िटर पैटर्न द्वारा आवश्यक बॉयलरप्लेट को बहुत कम करता है) या सी # में यूनियन प्रकार के कार्यान्वयन को याद करता है (मुझे याद है SOJ पर कहीं @ जुलिएट द्वारा एक सुंदर कार्यान्वयन) –

+0

@ क्रिस शैन: यह डबल-प्रेषण समाधान के लाभ का हिस्सा है - इसे नहीं करना होगा। 'डेटटाइम' के लिए, या तो डबल-डिस्पैच समाधान इसे अनदेखा करता है या संस्करण समाधान इसे 0 मान के रूप में उपयोग करता है। – redman

उत्तर

3

विधि 1, डबल प्रेषण के लिए गतिशील का उपयोग करके (क्रेडिट http://blogs.msdn.com/b/curth/archive/2008/11/15/c-dynamic-and-multiple-dispatch.aspx पर जाता है)। मूल रूप से आप अपने आगंतुक पैटर्न इस तरह सरलीकृत कर सकते हैं:

class Evaluator { 
public void Evaluate(IEnumerable<ITimedValue> values) { 
    foreach(var v in values) 
    { 
     Eval((dynamic)(v)); 
    } 
} 

private void Eval(DateTimeValue d) { 
    Console.WriteLine(d.Value.ToString() + " is a datetime"); 
} 

private void Eval(NumericValue f) { 
    Console.WriteLine(f.Value.ToString() + " is a float"); 
} 

} 

उपयोग के नमूना:

var l = new List<ITimedValue>(){ 
    new NumericValue(){Value= 5.1F}, 
    new DateTimeValue() {Value= DateTime.Now}}; 

new Evaluator() 
    .Evaluate(l); 
     // output: 
     // 5,1 is a float 
     // 29/02/2012 19:15:16 is a datetime 

विधि 2 संघ प्रकार का प्रयोग करेंगे ग # में के रूप में @Juliet here द्वारा प्रस्तावित (वैकल्पिक कार्यान्वयन here)

+0

खुशी है कि आप वापस आये और इन्हें पोस्ट किया, मैं उन्हें नहीं ढूंढ सका। और "variant" शब्द के गलत उपयोग को सही करने के लिए धन्यवाद। – redman

+0

मैंने उन्हें वेरिएंट प्रकार भी कहा है। और योग प्रकार और (शायद गलत है?) बीजगणितीय डेटा प्रकार। मैं किसी भी तरह से एक विशेषज्ञ नहीं हूं इसलिए मैं आपको सही करने की कोशिश नहीं कर रहा था: "यूनियन प्रकार" बस उस तरीके से होता है जिस तरह से वे आपको जवाब देने की कोशिश कर रहे थे। :) –

0

मैं आपको बताता हूं कि मैंने एक समान स्थिति हल की है - संग्रह में दोगुनी के रूप में डेटटाइम या टाइमस्पेन के Ticks को संग्रहित करके और प्रकार पैरामीटर पर जहां बाधा के रूप में IComparable का उपयोग करके है। डबल/डबल से रूपांतरण एक सहायक वर्ग द्वारा किया जाता है।

कृपया this previous question देखें।

मज़ाकिया रूप से यह मुक्केबाजी और अनबॉक्सिंग जैसी अन्य समस्याओं का कारण बनता है। जिस एप्लिकेशन पर मैं काम कर रहा हूं उसे अत्यधिक प्रदर्शन की आवश्यकता है इसलिए मुझे मुक्केबाजी से बचने की जरूरत है। यदि आप सामान्य रूप से विभिन्न डेटाटाइप (डेटटाइम सहित) को संभालने के लिए एक शानदार तरीका सोच सकते हैं तो मैं सभी कान हूं!

0

क्यों आप वास्तव में इच्छित इंटरफ़ेस को लागू नहीं करते हैं, और कार्यान्वयन प्रकार को यह निर्धारित करने की अनुमति देते हैं कि मूल्य क्या है?उदाहरण के लिए:

class NumericValue : ITimedValue<float> { 
public TimeSpan TimeStamp { get; private set; } 
public float Value { get; private set; } 
} 

class DateTimeValue : ITimedValue<DateTime>, ITimedValue<float> { 
public TimeSpan TimeStamp { get; private set; } 
public DateTime Value { get; private set; } 
public Float ITimedValue<Float>.Value { get { return 0; } } 
} 

class NumericEvaluator { 
public void Evaluate(IEnumerable<ITimedValue<float>> values) ... 
} 

आप दिनांक समय कार्यान्वयन के व्यवहार चाहते हैं विशेष रूप से उपयोग के आधार पर भिन्न करने के लिए (जैसे कि, कार्यों का मूल्यांकन की वैकल्पिक कार्यान्वयन), तो वे परिभाषा से ITimedValue<DateTime> के बारे में पता होना चाहिए। उदाहरण के लिए, आप एक या अधिक Converter प्रतिनिधि प्रदान करके एक अच्छे स्थैतिक-टाइप किए गए समाधान प्राप्त कर सकते हैं।

class NumericEvaluator { 
    public void Evaluate(IEnumerable<ITimedValue> values) { 
     foreach (NumericValue value in values.OfType<NumericValue>()) { 
      .... 
     } 
    } 
} 
+0

मैंने वास्तव में 'ऑफ टाइप' का उपयोग करने पर विचार किया, लेकिन मुझे याद नहीं आया कि मैं इसके साथ क्यों नहीं गया। नोट्स के माध्यम से खोदने पर, मुझे एक ऐसा मामला मिला जहां एक अलग प्रकार के डेटा को मार्कर के रूप में कभी-कभी दिखाने की उम्मीद थी। मैं संग्रह के लिए कई बार संग्रह की गणना कर सकता हूं, लेकिन मैं अक्षमता से बचना पसंद करूंगा (यह एक वास्तविक समय प्रणाली है)। – redman

+0

'मूल्यों के बारे में कैसे। (V => v एक्स है || वी वाई है)'? हालांकि फिर से मैं पूछता हूं कि एक्स और वाई के एक विषम गणना के साथ आप क्या करना चाहते हैं, अगर उनके पास सामान्य आधार प्रकार या इंटरफ़ेस नहीं है। –

+0

इस मामले में, वे पहले से मौजूद समय टिकट के बाहर डेटा में एक अस्थायी मीट्रिक इंजेक्ट करने का प्रयास कर रहे थे। – redman

0

अच्छा प्रश्न:

अंत में, यदि आप वास्तव में केवल NumericValue उदाहरणों हैंडल करना चाहते हैं, बस बाहर कुछ भी है कि एक NumericValue उदाहरण नहीं है फ़िल्टर करें। मेरे दिमाग में पहली चीज एक प्रतिबिंबित रणनीति एल्गोरिदम थी। रनटाइम संदर्भ को पकड़ने के लिए उपयोग किए जा रहे चर के प्रकार के बावजूद संदर्भ के सबसे व्युत्पन्न प्रकार, स्थिर या गतिशील रूप से आपको बता सकता है। हालांकि, दुर्भाग्यवश, यह व्युत्पन्न प्रकार, केवल परिवर्तनीय प्रकार के आधार पर स्वचालित रूप से ओवरलोड लोड नहीं करेगा। इसलिए, हमें रनटाइम पर पूछना होगा कि सही प्रकार क्या है, और उस पर आधारित, मैन्युअल रूप से एक विशेष अधिभार का चयन करें। प्रतिबिंब का उपयोग करके, हम गतिशील रूप से किसी विशेष उप-प्रकार को संभालने के रूप में पहचाने गए तरीकों का संग्रह बना सकते हैं, फिर उसके सामान्य प्रकार के संदर्भ के बारे में पूछताछ कर सकते हैं और उस पर आधारित शब्दकोश में कार्यान्वयन को देख सकते हैं।

public interface ITimedValueEvaluator 
{ 
    void Evaluate(ITimedValue value); 
} 

public interface ITimedValueEvaluator<T>:ITimedValueEvaluator 
{ 
    void Evaluate(ITimedValue<T> value); 
} 

//each implementation is responsible for implementing both interfaces' methods, 
//much like implementing IEnumerable<> requires implementing IEnumerable 
class NumericEvaluator: ITimedValueEvaluator<int> ... 

class DateTimeEvaluator: ITimedValueEvaluator<DateTime> ... 

public class Evaluator 
{ 
    private Dictionary<Type, ITimedValueEvaluator> Implementations; 

    public Evaluator() 
    { 
     //find all implementations of ITimedValueEvaluator, instantiate one of each 
     //and store in a Dictionary 
     Implementations = (from t in Assembly.GetCurrentAssembly().GetTypes() 
     where t.IsAssignableFrom(typeof(ITimedValueEvaluator<>) 
     and !t.IsInterface 
     select new KeyValuePair<Type, ITimedValueEvaluator>(t.GetGenericArguments()[0], (ITimedValueEvaluator)Activator.CreateInstance(t))) 
     .ToDictionary(kvp=>kvp.Key, kvp=>kvp.Value);  
    } 

    public void Evaluate(ITimedValue value) 
    { 
     //find the ITimedValue's true type's GTA, and look up the implementation 
     var genType = value.GetType().GetGenericArguments()[0]; 

     //Since we're passing a reference to the base ITimedValue interface, 
     //we will call the Evaluate overload from the base ITimedValueEvaluator interface, 
     //and each implementation should cast value to the correct generic type. 
     Implementations[genType].Evaluate(value); 
    } 

    public void Evaluate(IEnumerable<ITimedValue> values) 
    { 
     foreach(var value in values) Evaluate(value); 
    } 
} 

ध्यान दें कि मुख्य मूल्यांकनकर्ता केवल एकमात्र ऐसा है जो एक आईनेमेरेबल को संभाल सकता है; प्रत्येक ITimedValueEvalalu कार्यान्वयन एक समय में मूल्यों को संभालना चाहिए। यदि यह व्यवहार्य नहीं है (कहें कि आपको किसी विशेष प्रकार के सभी मानों पर विचार करने की आवश्यकता है), तो यह वास्तव में आसान हो जाता है; डिक्शनरी में प्रत्येक कार्यान्वयन के माध्यम से बस लूप करें, इसे पूर्ण आईनेमरेबल पास कर दें, और इन कार्यान्वयनों में सूची को ऑफटाइप() लिंक विधि का उपयोग करके केवल विशेष बंद जेनेरिक प्रकार की वस्तुओं को फ़िल्टर करें। आपको सूची में मिलने वाले सभी ITimedValueEvalalu कार्यान्वयन को चलाने की आवश्यकता होगी, जो किसी सूची में किसी विशेष प्रकार की कोई वस्तु नहीं होने पर बर्बाद प्रयास है।

इसकी सुंदरता इसकी विस्तारशीलता है; ITimedValue के एक नए जेनेरिक क्लोजर का समर्थन करने के लिए, बस उसी प्रकार के ITimedValueEvaluator का एक नया कार्यान्वयन जोड़ें। मूल्यांकनकर्ता वर्ग इसे प्राप्त करेगा, एक प्रतिलिपि को तुरंत चालू करेगा, और इसका उपयोग करेगा। सबसे अधिक प्रतिबिंबित एल्गोरिदम की तरह, यह धीमा है, लेकिन वास्तविक प्रतिबिंबित हिस्सा एक बार का सौदा है।

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^