2013-07-05 5 views
18

साझा करने मैं 2 वर्गों है:लौटें विभिन्न प्रकार के दो संभावित वस्तुओं में से एक के लिए एक विधि

public class Articles 
{ 
    private string name; 

    public Articles(string name) 
    { 
     this.name = name; 
    } 

    public void Output() 
    { 
     Console.WriteLine("The class is: " + this.GetType()); 
     Console.WriteLine("The name is: " + name); 
    } 
} 

और

public class Questionnaire 
{ 
    private string name; 

    public Questionnaire(string name) 
    { 
     this.name = name; 
    } 

    public void Output() 
    { 
     Console.WriteLine("The class is: " + this.GetType()); 
     Console.WriteLine("The name is: " + name); 
    } 
} 

मैं एक विधि लिखना चाहते हैं, कि एक पूर्णांक लेता है (1 अर्थ Articles वापस किया जाना चाहिए, 2 अर्थ Questionnaire) और एक नाम।

इस विधि उन दो वर्गों में से एक का एक उदाहरण लौटाएगा:

public [What type??] Choose(int x, string name) 
    { 
     if (x == 1) 
     { 
      Articles art = new Articles(name); 
      return art; 
     } 
     if (x == 2) 
     { 
      Questionnaire ques = new Questionnaire(name); 
      return ques; 
     } 
    } 

क्या वापसी प्रकार उपयोग करना चाहिए, तो मैं परिणाम पर Output() कॉल कर सकते हैं?

+6

यदि आपको लगता है कि दोनों प्रकारों में (कई) चीजें आम हैं तो आपको उन्हें एक ही बेस क्लास से उत्तराधिकारी होना चाहिए या कम से कम एक ही इंटरफेस को लागू करना चाहिए। –

+0

इसे आमतौर पर [बतख टाइपिंग] कहा जाता है (http://en.wikipedia.org/wiki/Duck_typing) - निश्चित रूप से सी # के साथ संभव है (देखें ['गतिशील'] (http://msdn.microsoft.com/en-us/ पुस्तकालय/dd264741.aspx)), लेकिन उत्तर में दिखाए गए दृढ़ता से टाइप किए गए समाधानों का उपयोग करने पर विचार करें। –

उत्तर

27

क्यों base class नहीं है जिसमें Output परिभाषित किया गया है। फिर आधार वापस करें।

public abstract class BaseType { 
    public abstract void Output(); 
} 

दोनों Articles और Questionaire इस BaseType वारिस चाहिए।

public class Articles : BaseType { 
    // Output method here 
} 

public class Questionaire : BaseType { 
// Output method here 
} 

तो फिर तुम कर सकते हैं:

public static BaseType Choose(int x, string name) 
{ 
    if (x == 1) 
    { 
     Articles art = new Articles(name); 
     return art; 
    } 
    if (x == 2) 
    { 
     Questionnaire ques = new Questionnaire(name); 
     return ques; 
    } 
} 

तुम भी एक interface के माध्यम से इस लक्ष्य को हासिल कर सकते हैं।

public interface IInterface { 
    void Output(); 
} 

public class Articles : IInterface { 
    // Output method here 
} 

public class Questionaire : IInterface { 
// Output method here 
} 

फिर आप IInterface बजाय BaseType वापस जाने के लिए चुनें विधि को संशोधित करने के लिए होगा। जो भी आप चुनते हैं वह आपके ऊपर है।

नोट:

public class ArticlesProxy : Articles, IInterface 
{ 
    public ArticlesProxy(string name) : base(name){} 

} 

public class QuestionaireProxy : Questionaire, IInterface { 
    Questionaire inner; 
    public QuestionaireProxy(string name) { inner = new Questionaire(name); } 

    public void Output() { inner.Output();} 

} 
+0

एक अच्छी प्रतिभा या तो बेस प्रकार का उपयोग करती है, जैसे एक प्रिंट करने योग्य वर्ग, प्रति उदाहरण या आईप्रिंटेबल जैसे इंटरफ़ेस। –

+1

+1 क्योंकि यह एक इंटरफ़ेस के रूप में मान्य है। –

+0

ध्यान में रखते हुए कि मैंने उस नाम का उपयोग किया है, इसलिए यह आपकी कक्षाओं से संबंधित है –

5

जब तक वे समान बेस क्लास या इंटरफ़ेस साझा नहीं करते हैं, तो आप object या dynamic के साथ बहुत अधिक अटक जाते हैं।

+0

यह वास्तव में कभी भी असफल नहीं है - यह सिर्फ खराब कोड डिज़ाइन है – TGlatzer

+3

मैं सुझाव नहीं दे रहा था कि यह अच्छा डिज़ाइन था। केवल इतना ही कहा गया है कि डिजाइन के साथ, वे विकल्प हैं। –

15
: भले ही आप मूल वर्गों को बदल नहीं सकते आप अभी भी आवरण वर्गों कि इंटरफ़ेस को लागू है और या तो मूल विरासत में या आगे इसी विधि करने के लिए कॉल प्रदान करके dynamic का सहारा से पहले इन तरीकों का उपयोग कर सकते

कैसे कुछ इस तरह के बारे में:

public interface IHasOutput 
{ 
    void Output(); 
} 

public class Articles : IHasOutput 

public class Questionnaire : IHasOutput 

और उसके बाद:

public static IHasOutput Choose... 

IHasOutput के अलावा, आप बस अपने इंटरफ़ेस को कुछ भी कॉल कर सकते हैं, मुझे नहीं पता कि उसे क्या कॉल करना है। यह इंटरफेस के लिए क्या है। एक आम इंटरफेस साझा करने वाले दो अलग ठोस कार्यान्वयन। अब जब आप इसे कहते आप यह कर सकते हैं:

var entity = MyClass.Choose(1, "MyName"); 
entity.Output(); 

और यह कोई बात नहीं क्या ठोस कार्यान्वयन दिया जाता है। आप जानते हैं कि यह एक सामान्य इंटरफ़ेस लागू करता है।

1) प्रश्नावली करें और अनुच्छेद एक ही आधार वर्ग से विरासत और उस आधार वर्ग के प्रकार के अपने विधि की वापसी प्रकार हो कर:

+0

+1 एक ही लिख रहा था समाधान जब आपका दिखाई दिया !! grrr :) –

+0

@jimtollan, महान दिमाग एक जैसे सोचते हैं! –

+1

आपको 'IHasOutput' को 'आईएनएनटीटी'' का नाम बदलना चाहिए, क्योंकि यह पूरे वर्ग के एक सामान्य * व्यवहार * नहीं बल्कि एक विशिष्ट * व्यवहार * का प्रतिनिधित्व करता है। लेकिन यह अभी भी एक बहुत ही वैध जवाब है। –

0

आप 3 विकल्प हैं।

2) अपनी वापसी प्रकार ऑब्जेक्ट बनाएं।

3) अपनी वापसी प्रकार गतिशील बनाएं।

1

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

public override string ToString() 
{ 
    return String.Format("The class is: {0}\r\nThe name is: {1}", 
         this.GetType(), _name); 
} 

आप इसे कहेंगे: इस व्युत्पन्न वर्ग

public interface IHasOutput 
{ 
    void Output(); 
} 

public abstract class OutputBase : IHasOutput 
{ 
    protected string _name; 

    public OutputBase(string name) 
    { 
     _name = name; 
    } 

    #region IHasOutput Members 

    public virtual void Output() 
    { 
     Console.WriteLine("The class is: " + this.GetType()); 
     Console.WriteLine("The name is: " + _name); 
    } 

    #endregion 

    public static IHasOutput Choose(int x, string name) 
    { 
     switch (x) { 
      case 1: 
       return new Articles(name); 
      case 2: 
       return new Questionnaire(name); 
      default: 
       return null; 
     } 
    } 
} 

public class Articles : OutputBase 
{ 
    public Articles(string name) 
     : base(name) 
    { 
    } 
} 

public class Questionnaire : OutputBase 
{ 
    public Questionnaire(string name) 
     : base(name) 
    { 
    } 
} 

अद्यतन

समस्या को हल करने के लिए एक बहुत ही आसान तरीका ToString ओवरराइड करने के लिए है में इसका इस्तेमाल करने के लिए सक्षम बनाता है इस तरह:

object obj = Factory.Choose(1, "Test"); 
Console.WriteLine(obj); 

कोई इंटरफ़ेस नहीं और कोई बेस क्लास आवश्यक नहीं है! खैर, सटीक होने के लिए, बेस क्लास object बेशक है।

7

यहां दिए गए उत्तर यहां बहुत अच्छे हैं, लेकिन एक चीज़ जो मुझे पसंद नहीं है पैरामीटर x है जो चुनता है कि किस प्रकार का निर्माण किया जाना चाहिए। इससे magic number का उपयोग होता है, जो बाद में आपके लिए भी सिर दर्द हो सकता है।

आप यहाँ जेनरिक का लाभ ले सकते हैं, जैसे कि बनाने के विधि Choose:

public static T Choose<T>(string name) 
     // type constraint to ensure hierarchy. 
     where T : BaseClass // BaseClass have common functionality of both class. 
    { 
     // Unfortunately you can't create instance with generic and pass arguments 
     // to ctor. So you have to use Activator here. 
     return (T)Activator.CreateInstance(typeof(T), new[] { name }); 
    } 

उपयोग:

Articles article = ClassWithChooseMethod.Choose<Articles>("name"); 
Questionnaire questionnaire = ClassWithChooseMethod.Choose<Questionnaire>("name2"); 

Demo

संपादित

@OlivierJaco के रूप में टी 0 Des39bes टिप्पणी x में उल्लिखित है जो प्रकार चुनता है उपयोगकर्ता इनपुट हो सकता है। उस मामले में आप संबंधित मूल्यों के साथ enum बना सकते हैं:

enum ArticleType { 
    Articles = 1, 
    Questionnaire = 2 
} 

और Choose का अधिभार है:

public static BaseClass Choose(ArticleType type, string name) { 
    switch (type) { 
     case ArticleType.Articles: 
      return ClassWithChooseMethod.Choose<Articles>(name); 
     case ArticleType.Questionnaire: 
      return ClassWithChooseMethod.Choose<Questionnaire>(name); 
     default: 
      return default(BaseClass); 
    } 
} 

और उपयोग: अपने कोड क्लीनर रखने के लिए

var obj = ClassWithChooseMethod.Choose((ArticleType)userInput, "some name"); 

यह आपको संभावना प्रदान करता है और भविष्य के रखरखाव के लिए उपयोगी (उदाहरण के लिए आप Choose में कक्षा निर्माण के तर्क को बदल सकते हैं)।

पीएस आपको factory pattern के बारे में और अधिक पढ़ने में रुचि हो सकती है।

+0

और आपको 'टी' टाइप कहां से मिलता है? सबसे अधिक संभावना है कि 'x' का मान हार्ड-कोड नहीं है, लेकिन उपयोगकर्ता इनपुट से आता है। –

+0

@ ओलिवियर जैकोट-डेस्कोम्ब्स शायद यह हो सकता है। ओपी में इसका उल्लेख नहीं किया गया था। और यहां तक ​​कि यदि यह है, तो मैं प्रकारों को संभालने के लिए 'enum' बनाउंगा और' चुनें 'विधि के बाहर' स्विच' का उपयोग करूंगा या मेरे पास इसका गैर-जेनेरिक अधिभार होगा जो आंतरिक प्रकार को वापस करने के लिए आंतरिक विधि का आंतरिक रूप से उपयोग करेगा। – Leri

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