सामान्य विधि कई (या) प्रकार की बाधा


135

पढ़ना यह , मुझे पता चला यह संभव था एक विधि यह एक सामान्य विधि बनाकर कई प्रकार के पैरामीटर स्वीकार करने की अनुमति। उदाहरण में, निम्नलिखित कोड का उपयोग "U" सुनिश्चित करने के लिए एक प्रकार की बाधा के साथ किया जाता है IEnumerable<T>

public T DoSomething<U, T>(U arg) where U : IEnumerable<T>
{
    return arg.First();
}

मुझे कुछ और कोड मिले जिन्होंने कई प्रकार की बाधाओं को जोड़ने की अनुमति दी, जैसे:

public void test<T>(string a, T arg) where T: ParentClass, ChildClass 
{
    //do something
}

हालाँकि, यह कोड लागू होता argहै जो एक प्रकार का ParentClass और दोनों होना चाहिए ChildClass। मैं यह कहना चाहता हूं कि arg एक प्रकार का ParentClass या ChildClass निम्नलिखित तरीके से हो सकता है:

public void test<T>(string a, T arg) where T: string OR Exception
{
//do something
}

आपकी मदद हमेशा की तरह सराहना की है!


4
ऐसी विधि के शरीर के भीतर एक सामान्य तरीके से आप उपयोगी क्या कर सकते हैं (जब तक कि कई प्रकार सभी एक विशिष्ट आधार वर्ग से नहीं निकलते हैं, उस स्थिति में इसे प्रकार की बाधा के रूप में घोषित क्यों नहीं किया जाता है)?
Damien_The_Unbeliever

@Damien_The_Unbeliever यकीन नहीं है कि आप शरीर में क्या मतलब है? जब तक आप किसी भी प्रकार की अनुमति नहीं देते हैं और मैन्युअल रूप से शरीर में जांच कर रहे हैं ... और वास्तविक कोड में मैं लिखना चाहता हूं (अंतिम कोड स्निपेट), मैं एक स्ट्रिंग या अपवाद पारित करने में सक्षम होना चाहता हूं, इसलिए कोई वर्ग नहीं है वहाँ रिश्ता (सिवाय system.object मैं कल्पना के)।
मैन्सफील्ड

1
यह भी ध्यान दें कि लेखन में कोई उपयोग नहीं है where T : string, जैसा stringकि एक सील वर्ग है। केवल उपयोगी चीज जो आप कर सकते हैं , नीचे दिए गए अपने उत्तर में @ Botz3000 द्वारा समझाया गया है stringऔर के लिए ओवरलोड को परिभाषित कर रहा है T : Exception
मटियास बुएलेंस ने

लेकिन जब कोई संबंध नहीं होता है, तो आप argजिन तरीकों से कॉल कर सकते हैं, वे केवल objectउसी के द्वारा परिभाषित होते हैं - तो क्यों न केवल चित्र से जेनेरिक को हटा दें और किस प्रकार का बनाएं arg object? आप क्या हासिल कर रहे हैं?
Damien_The_Unbeliever

1
@ मैन्सफील्ड आप एक निजी विधि बना सकते हैं जो ऑब्जेक्ट पैरामीटर को स्वीकार करता है। दोनों अधिभार उस एक को कहेंगे। यहां किसी जेनरिक की जरूरत नहीं है।
बॉटज़3000

जवाबों:


68

यह संभव नहीं है। हालाँकि, आप विशिष्ट प्रकारों के लिए अधिभार परिभाषित कर सकते हैं:

public void test(string a, string arg);
public void test(string a, Exception arg);

यदि वे एक सामान्य वर्ग का हिस्सा हैं, तो उन्हें विधि के सामान्य संस्करण पर पसंद किया जाएगा।


1
दिलचस्प। यह मेरे साथ हुआ था, लेकिन मैंने सोचा कि यह एक फ़ंक्शन का उपयोग करने के लिए कोड क्लीनर बना सकता है। आह ठीक है, बहुत बहुत धन्यवाद! जिज्ञासा से बाहर, क्या आपको पता है कि क्या कोई विशिष्ट कारण यह संभव नहीं है? (क्या इसे भाषा से जानबूझकर छोड़ दिया गया है?)
मैंसफील्ड

3
@ मैन्सफील्ड मुझे सटीक कारण नहीं पता है, लेकिन मुझे लगता है कि आप सामान्य तरीके से सामान्य मानकों का उपयोग नहीं कर पाएंगे। कक्षा के अंदर, उन्हें वस्तु की तरह माना जाएगा यदि उन्हें पूरी तरह से अलग प्रकार का होने दिया जाए। इसका मतलब है कि आप सामान्य पैरामीटर को छोड़ सकते हैं और ओवरलोड प्रदान कर सकते हैं।
बॉटज़ ३०००

3
@ मैन्सफील्ड, ऐसा इसलिए है क्योंकि एक orसंबंध उपयोगी होने के लिए चीजों को सामान्य बनाता है। आप चाहते हैं के लिए है कि क्या करना है और जो कुछ यह पता लगाने की प्रतिबिंब करने के लिए। (छी!)।
क्रिस Pfohl

29

बॉटज़ का उत्तर 100% सही है, यहाँ एक संक्षिप्त विवरण दिया गया है:

जब आप एक विधि (जेनेरिक या नहीं) लिख रहे हैं और उन प्रकार के मापदंडों की घोषणा कर रहे हैं जो विधि आपको ले रही है, तो अनुबंध को परिभाषित करना:

यदि आप मुझे एक वस्तु देते हैं जो जानता है कि टाइप टी के सेट को कैसे करना है, तो पता है कि मैं कैसे कर सकता हूं कि मैं या तो 'ए' प्रदान कर सकता हूं: मेरे द्वारा घोषित प्रकार का एक वापसी मूल्य, या 'बी': कुछ प्रकार का व्यवहार जो उपयोग करता है वह प्रकार।

यदि आप कोशिश करते हैं और इसे एक समय में एक से अधिक प्रकार देते हैं (या एक) या इसे प्राप्त करने का प्रयास करते हैं तो एक मूल्य से अधिक हो सकता है जो अनुबंध से अधिक हो सकता है:

यदि आप मुझे एक ऐसी वस्तु देते हैं जो रस्सी कूदना जानती है या 15 वें अंक में पाई की गणना करना जानती है तो मैं या तो एक वस्तु वापस करूंगा जो मछली पकड़ने जा सकती है या शायद कंक्रीट को मिला सकती है।

समस्या यह है कि जब आप विधि में आते हैं तो आपको पता नहीं होता है कि उन्होंने आपको एक IJumpRopeया एक दिया है PiFactory। इसके अलावा, जब आप आगे बढ़ते हैं और विधि का उपयोग करते हैं (यह मानते हुए कि आपने इसे जादुई रूप से संकलित कर लिया है) यदि आप एक Fisherया एक हैं तो आप वास्तव में निश्चित नहीं हैं AbstractConcreteMixer। मूल रूप से यह पूरी तरह से अधिक भ्रमित करने का रास्ता बनाता है।

आपकी समस्या का समाधान दो व्यवसायों में से एक है:

  1. एक से अधिक तरीकों को परिभाषित करें जो प्रत्येक संभव परिवर्तन, व्यवहार, या जो कुछ भी परिभाषित करता है। यह बॉटज का जवाब है। प्रोग्रामिंग की दुनिया में इस विधि को ओवरलोडिंग कहा जाता है।

  2. आधार वर्ग या इंटरफ़ेस को परिभाषित करें जो जानता है कि उन सभी चीजों को कैसे करना है जो आपको विधि के लिए आवश्यक हैं और एक विधि है बस उस प्रकार को लें। इसमें एक छोटी कक्षा में रैपिंग शामिल है stringऔर Exceptionयह परिभाषित करने के लिए कि आप उन्हें लागू करने के लिए मैपिंग की योजना कैसे बनाते हैं, लेकिन फिर सब कुछ सुपर स्पष्ट और पढ़ने में आसान है। मैं अब से चार साल बाद आ सकता हूं और आपका कोड पढ़ सकता हूं और आसानी से समझ सकता हूं कि क्या हो रहा है।

जो आप चुनते हैं वह इस बात पर निर्भर करता है कि विकल्प 1 और 2 कितना जटिल होगा और इसे कितना एक्सटेंसिबल होना चाहिए।

इसलिए आपकी विशिष्ट स्थिति के लिए मैं कल्पना कर रहा हूं कि आप केवल एक संदेश या अपवाद से कुछ निकाल रहे हैं:

public interface IHasMessage
{
    string GetMessage();
}

public void test(string a, IHasMessage arg)
{
    //Use message
}

अब आप सभी की जरूरत है कि एक stringऔर Exceptionएक IHasMessage में बदलने के तरीके हैं । बहुत आसान।


क्षमा करें @ बोटज़ ३०००, बस मैंने देखा कि मैंने आपका नाम याद किया।
क्रिस Pfohl

या कंपाइलर फ़ंक्शन के भीतर टाइप को एक यूनियन प्रकार के रूप में धमकी दे सकता है और रिटर्न वैल्यू उसी प्रकार का हो सकता है जो इसे मिलता है। टाइपस्क्रिप्ट यह करता है।
एलेक्स

@ एलेक्स, लेकिन वह नहीं है जो सी # करता है।
क्रिस Pfohl

यह सच है, लेकिन यह हो सकता है। मैंने यह उत्तर इसलिए पढ़ा क्योंकि यह गलत नहीं होगा, क्या मैंने गलत व्याख्या की?
एलेक्स

1
बात यह है कि सी # जेनेरिक पैरामीटर बाधाएं और जेनेरिक स्वयं सी, सी + + टेम्पलेट्स की तुलना में बहुत आदिम हैं। सी # की आवश्यकता है कि आप पहले से संकलक को बताएं कि जेनेरिक प्रकारों पर कौन से संचालन की अनुमति है। उस जानकारी को प्रदान करने का तरीका एक कार्यान्वयन इंटरफ़ेस बाधा जोड़ना है (जहां T: IDis प्रयोज्य)। लेकिन आप अपने प्रकार को सामान्य इंटरफ़ेस का उपयोग करने के लिए कुछ इंटरफ़ेस को लागू करने के लिए नहीं कर सकते हैं या आप अपने सामान्य कोड में कुछ प्रकारों की अनुमति देना चाहते हैं जिनके पास सामान्य इंटरफ़ेस नहीं है। पूर्व। किसी भी संरचना या स्ट्रिंग की अनुमति दें ताकि आप बस मूल्य आधारित तुलना के लिए इक्वाल्स (v1, वी 2) को कॉल कर सकें।
वख्तंग १४'१

8

यदि चाइल्डक्लास का अर्थ पेरेंटक्लास से लिया गया है, तो आप केवल पेरेंटक्लास और चाइल्डक्लास दोनों को स्वीकार करने के लिए निम्नलिखित लिख सकते हैं;

public void test<T>(string a, T arg) where T: ParentClass 
{
    //do something
}

दूसरी तरफ, यदि आप दो अलग-अलग प्रकारों का उपयोग करना चाहते हैं, जिनके बीच कोई विरासत संबंध नहीं है, तो आपको एक ही इंटरफ़ेस को लागू करने वाले प्रकारों पर विचार करना चाहिए;

public interface ICommonInterface
{
    string SomeCommonProperty { get; set; }
}

public class AA : ICommonInterface
{
    public string SomeCommonProperty
    {
        get;set;
    }
}

public class BB : ICommonInterface
{
    public string SomeCommonProperty
    {
        get;
        set;
    }
}

फिर आप अपना सामान्य कार्य लिख सकते हैं;

public void Test<T>(string a, T arg) where T : ICommonInterface
{
    //do something
}

अच्छा विचार, मुझे नहीं लगता कि मैं ऐसा कर पाऊंगा क्योंकि मैं स्ट्रिंग का उपयोग करना चाहता हूं जो कि ऊपर की टिप्पणी के अनुसार एक मुहरबंद वर्ग है ...
मैन्सफील्ड

यह वास्तव में एक डिजाइन समस्या है। जेनेरिक फ़ंक्शन का उपयोग कोड पुन: उपयोग के साथ समान संचालन करने के लिए किया जाता है। यदि आप मेथड बॉडी में अलग-अलग ऑपरेशन करने की योजना बना रहे हैं, तो तरीकों को अलग करना एक बेहतर तरीका है (IMHO)।
दरियाल

मैं वास्तव में क्या कर रहा हूं, एक साधारण त्रुटि लॉगिंग फ़ंक्शन लिख रहा है। मुझे लगता है कि अंतिम पैरामीटर या तो त्रुटि, या अपवाद के बारे में जानकारी का एक स्ट्रिंग होना चाहिए, जिस स्थिति में मैं e.message + e.stacktrace को वैसे भी एक स्ट्रिंग के रूप में सहेजता हूं।
मैन्सफील्ड

आप एक नया वर्ग लिख सकते हैं, होने के नाते, गुण के रूप में संदेश और अपवाद को बचा सकते हैं। तो आप जांच सकते हैं कि क्या जारी करने योग्य सच है और शेष कार्य करें।
दरियाल

1

यह सवाल जितना पुराना है, मुझे अभी भी ऊपर दिए गए स्पष्टीकरण पर रैंडम अपवोट मिलते हैं। स्पष्टीकरण अभी भी पूरी तरह से ठीक है जैसा कि यह है, लेकिन मैं दूसरी बार एक ऐसे प्रकार के साथ जवाब देने जा रहा हूं जो मुझे अच्छी तरह से संघ प्रकारों के विकल्प के रूप में परोसा गया है (इस सवाल का दृढ़ता से टाइप किया गया उत्तर जो सीधे # सी द्वारा समर्थित नहीं है। )।

using System;
using System.Diagnostics;

namespace Union {
    [DebuggerDisplay("{currType}: {ToString()}")]
    public struct Either<TP, TA> {
        enum CurrType {
            Neither = 0,
            Primary,
            Alternate,
        }
        private readonly CurrType currType;
        private readonly TP primary;
        private readonly TA alternate;

        public bool IsNeither => currType == CurrType.Primary;
        public bool IsPrimary => currType == CurrType.Primary;
        public bool IsAlternate => currType == CurrType.Alternate;

        public static implicit operator Either<TP, TA>(TP val) => new Either<TP, TA>(val);

        public static implicit operator Either<TP, TA>(TA val) => new Either<TP, TA>(val);

        public static implicit operator TP(Either<TP, TA> @this) => @this.Primary;

        public static implicit operator TA(Either<TP, TA> @this) => @this.Alternate;

        public override string ToString() {
            string description = IsNeither ? "" :
                $": {(IsPrimary ? typeof(TP).Name : typeof(TA).Name)}";
            return $"{currType.ToString("")}{description}";
        }

        public Either(TP val) {
            currType = CurrType.Primary;
            primary = val;
            alternate = default(TA);
        }

        public Either(TA val) {
            currType = CurrType.Alternate;
            alternate = val;
            primary = default(TP);
        }

        public TP Primary {
            get {
                Validate(CurrType.Primary);
                return primary;
            }
        }

        public TA Alternate {
            get {
                Validate(CurrType.Alternate);
                return alternate;
            }
        }

        private void Validate(CurrType desiredType) {
            if (desiredType != currType) {
                throw new InvalidOperationException($"Attempting to get {desiredType} when {currType} is set");
            }
        }
    }
}

ऊपर वर्ग एक प्रकार है कि हो सकता है का प्रतिनिधित्व करता है या तो टी.पी. या प्रादेशिक सेना। आप इसका उपयोग इस प्रकार कर सकते हैं (प्रकार मेरे मूल उत्तर पर वापस जाएँ):

// ...
public static Either<FishingBot, ConcreteMixer> DemoFunc(Either<JumpRope, PiCalculator> arg) {
  if (arg.IsPrimary) {
    return new FishingBot(arg.Primary);
  }
  return new ConcreteMixer(arg.Secondary);
}

// elsewhere:

var fishBotOrConcreteMixer = DemoFunc(new JumpRope());
var fishBotOrConcreteMixer = DemoFunc(new PiCalculator());

महत्वपूर्ण लेख:

  • यदि आप IsPrimaryपहले जाँच नहीं करते हैं तो आपको रनटाइम त्रुटियाँ मिलेंगी ।
  • आप किसी भी IsNeither IsPrimaryया जाँच कर सकते हैं IsAlternate
  • आप के माध्यम से Primaryऔर मूल्य का उपयोग कर सकते हैंAlternate
  • टीपी / टीए और दोनों के बीच निहित कन्वर्टर्स हैं जो आपको या तो मूल्यों या Eitherकहीं भी पारित करने की अनुमति देने के लिए जहां एक की उम्मीद है। यदि आप एक ऐसा स्थान पास करते हैं Eitherजहाँ एक TAया TPअपेक्षित है, लेकिन Eitherइसमें गलत प्रकार का मान है, तो आपको रनटाइम त्रुटि मिलेगी।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.