क्‍यूटिक <T> के बजाय फंक <टी, बूल> क्‍यों?


210

यह सिर्फ एक जिज्ञासा का सवाल है जो मैं सोच रहा था कि किसी के पास एक अच्छा जवाब था:

.NET फ्रेमवर्क क्लास लाइब्रेरी में उदाहरण के लिए हमारे पास ये दो विधियाँ हैं:

public static IQueryable<TSource> Where<TSource>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, bool>> predicate
)

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate
)

वे Func<TSource, bool>इसके बजाय क्यों उपयोग करते हैं Predicate<TSource>? ऐसा लगता है कि Predicate<TSource>केवल द्वारा उपयोग किया जाता है List<T>और Array<T>, जबकि Func<TSource, bool>बहुत अधिक सभी Queryableऔर Enumerableविधियों और विस्तार विधियों द्वारा उपयोग किया जाता है ... उस के साथ क्या हो रहा है?


21
उह हाँ, इन असंगत उपयोग मुझे पागल भी करते हैं।
जॉर्ज मौअर

जवाबों:


170

जबकि Predicateएक ही समय में पेश किया गया है कि List<T>और Array<T>.net 2.0 में, अलग Funcऔर Actionवेरिएंट .net 3.5 से आते हैं।

तो उन Funcविधेयकों का उपयोग मुख्य रूप से LINQ ऑपरेटरों में स्थिरता के लिए किया जाता है। .Net 3.5, उपयोग के बारे में के रूप में Func<T>और दिशानिर्देश राज्यों :Action<T>

नए LINQ प्रकारों का उपयोग करें Func<>और Expression<>इसके बजाय कस्टम प्रतिनिधियों और विधेयकों का उपयोग करें


7
कूल, कभी नहीं = पहले उन guildelines देखा)
Svish

6
इसे उत्तर के रूप में स्वीकार करेंगे, क्योंकि इसमें सबसे अधिक वोट हैं। और क्योंकि जॉन स्कीट के पास बहुत सारे प्रतिनिधि हैं ...: p
Svish

4
जैसा कि एक विचित्र दिशानिर्देश है। निश्चित रूप से यह बताना चाहिए कि "नए LINQ प्रकारों का उपयोग करें" फंक <> "और" एक्शन <> "[...]"। अभिव्यक्ति <> एक पूरी तरह से अलग चीज है।
जॉन स्कीट

3
ठीक है, वास्तव में आपको यह दिशानिर्देश के संदर्भ में रखना होगा, जो एक LINQ प्रदाता लिखने के बारे में है। तो हाँ, LINQ ऑपरेटरों के लिए, दिशानिर्देश फंक <> और अभिव्यक्ति <> विस्तार विधियों के लिए मापदंडों के रूप में उपयोग करना है। मैं सहमत हूं कि मैं फंक और एक्शन के बारे में एक अलग दिशानिर्देश की सराहना करूंगा
जेबी एवैन

मुझे लगता है कि स्कीट की बात यह है कि अभिव्यक्ति <> एक दिशानिर्देश नहीं है। यह उस प्रकार को पहचानने और इसके साथ कुछ अलग करने के लिए एक सी # संकलक सुविधा है।
डैनियल ईयरविकेर

116

मैंने पहले यह सोचा है। मुझे Predicate<T>प्रतिनिधि पसंद है - यह अच्छा और वर्णनात्मक है। हालाँकि, आपको ओवरलोड के बारे में विचार करना होगा Where:

Where<T>(IEnumerable<T>, Func<T, bool>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

यह आपको प्रविष्टि के सूचकांक के आधार पर भी फ़िल्टर करने की अनुमति देता है। यह अच्छा और सुसंगत है, जबकि:

Where<T>(IEnumerable<T>, Predicate<T>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

नहीं होगा


11
विधेय <int, बूल> कुछ बदसूरत होगा - एक विधेय आमतौर पर (कंप्यूटर विज्ञान का IME) एक ही मूल्य पर अनुमानित है। यह बेशक <Pair <T, int >> हो सकता है, लेकिन यह भी बदसूरत है :)
जॉन स्कीट

सच है ... हे। नहीं, मुझे लगता है कि फंक क्लीनर है।
शविश

2
दिशानिर्देशों के कारण अन्य उत्तर को स्वीकार किया। काश मैं एक से अधिक जवाबों को चिन्हित कर पाता! अच्छा होगा यदि मैं "बहुत उल्लेखनीय" या "बहुत अच्छा बिंदु होने के साथ ही उत्तर के अतिरिक्त ध्यान दिया जाना चाहिए"
Svish

6
हम्म, बस देखा कि मैंने लिखा है कि पहली टिप्पणी गलत तरीके से: पी मेरा सुझाव निश्चित रूप से प्रेडिक्ट होने जा रहा था <टी, int> ...
Svish

4
@JonSkeet मुझे पता है कि यह सवाल पुराना है और सभी है, लेकिन पता है कि क्या विडंबना है? Whereविस्तार विधि में पैरामीटर का नाम है predicate। हेह = पी।
कॉनरैड क्लार्क

32

निश्चित रूप Funcसे एक विशिष्ट प्रतिनिधि के बजाय उपयोग करने का वास्तविक कारण यह है कि सी # अलग-अलग घोषित प्रतिनिधियों को पूरी तरह से अलग प्रकार का मानता है।

भले ही Func<int, bool>और Predicate<int>दोनों के पास समान तर्क और वापसी प्रकार हैं, वे असाइनमेंट-संगत नहीं हैं। इसलिए यदि प्रत्येक पुस्तकालय प्रत्येक प्रतिनिधि पैटर्न के लिए अपने स्वयं के प्रतिनिधि प्रकार की घोषणा करता है, तो वे पुस्तकालय तब तक हस्तक्षेप नहीं कर पाएंगे जब तक उपयोगकर्ता रूपांतरण करने के लिए प्रतिनिधियों को "ब्रिजिंग" नहीं करता।

    // declare two delegate types, completely identical but different names:
    public delegate void ExceptionHandler1(Exception x);
    public delegate void ExceptionHandler2(Exception x);

    // a method that is compatible with either of them:
    public static void MyExceptionHandler(Exception x)
    {
        Console.WriteLine(x.Message);
    }

    static void Main(string[] args)
    {
        // can assign any method having the right pattern
        ExceptionHandler1 x1 = MyExceptionHandler; 

        // and yet cannot assign a delegate with identical declaration!
        ExceptionHandler2 x2 = x1; // error at compile time
    }

सभी को फंक का उपयोग करने के लिए प्रोत्साहित करके, Microsoft उम्मीद कर रहा है कि यह असंगत प्रतिनिधि प्रकार की समस्या को कम करेगा। सभी के प्रतिनिधि एक साथ अच्छी तरह से खेलेंगे, क्योंकि उनका मिलान उनके पैरामीटर / रिटर्न प्रकारों के आधार पर किया जाएगा।

यह सभी समस्याओं को हल नहीं करता है, क्योंकि Func(और Action) outया नहीं हो सकता हैref पैरामीटर , लेकिन वे कम सामान्यतः उपयोग किए जाते हैं।

अपडेट: टिप्पणी में Svish कहते हैं:

फिर भी, Func से Predicate और वापस करने के लिए एक पैरामीटर प्रकार स्विच करना, कोई अंतर नहीं लगता है? कम से कम यह अभी भी बिना किसी समस्या के संकलन करता है।

हां, जब तक आपका कार्यक्रम केवल प्रतिनिधियों को विधियां प्रदान करता है, जैसा कि मेरे Mainकार्य की पहली पंक्ति में है। संकलक चुपचाप एक नया प्रतिनिधि कोड उत्पन्न करता है जो विधि को आगे बढ़ाता है। इसलिए मेरे Mainकार्य में, मैं समस्या के कारण के बिना x1टाइप का ExceptionHandler2हो सकता है।

हालाँकि, दूसरी पंक्ति में मैं पहले प्रतिनिधि को दूसरे प्रतिनिधि को सौंपने की कोशिश करता हूँ। यहां तक ​​कि यह सोचा गया कि 2 प्रतिनिधि प्रकार में बिल्कुल समान पैरामीटर और रिटर्न प्रकार हैं, संकलक त्रुटि देता हैCS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'

शायद इससे यह स्पष्ट हो जाएगा:

public static bool IsNegative(int x)
{
    return x < 0;
}

static void Main(string[] args)
{
    Predicate<int> p = IsNegative;
    Func<int, bool> f = IsNegative;

    p = f; // Not allowed
}

जब तक मैं सीधे ऐसा करता हूं, तब तक और चर IsNegativeको असाइन करने के लिए मेरी विधि पूरी तरह से अच्छी चीज है । लेकिन तब मैं उनमें से किसी एक चर को दूसरे को नहीं सौंप सकता।pf


फिर भी, फनक <टी, बूल> से प्रॉमेट <टी> और पीछे से एक पैरामीटर प्रकार स्विच करने से कोई फर्क नहीं पड़ता है? कम से कम यह अभी भी बिना किसी समस्या के संकलन करता है।
शविश

लगता है कि एमएस डेवलपर्स को यही सोचने से रोकने की कोशिश कर रहे हैं। मुझे आश्चर्य है क्योंकि?
मैट कोकाज

1
पैरामीटर प्रकार को स्विच करने से फर्क पड़ता है यदि आप जिस अभिव्यक्ति से गुजर रहे हैं, उसे अलग-अलग तरीके से कॉल करने के लिए परिभाषित किया गया है, तब से यह कंपाइलर द्वारा टाइप किए गए टाइप के बजाय Func<T, bool>या तो टाइप किया जाएगा Predicate<T>
एडम राल्फ

30

सलाह (3.5 और इसके बाद के संस्करण) का उपयोग करने के लिए है Action<...>और Func<...>- "क्यों?" - एक फायदा यह है कि "Predicate<T> " केवल सार्थक है यदि आप जानते हैं कि "विधेय" का क्या अर्थ है - अन्यथा आपको साइन-पुट खोजने के लिए ऑब्जेक्ट-ब्राउज़र (आदि) को देखने की आवश्यकता है।

इसके विपरीत Func<T,bool>एक मानक पैटर्न का पालन करता है; मैं तुरंत बता सकता हूं कि यह एक फ़ंक्शन है जो ए लेता है Tऔर रिटर्न देता हैbool - किसी भी शब्दावली को समझने की आवश्यकता नहीं है - बस मेरी सत्य परीक्षा लागू करें।

"विधेय" के लिए यह ठीक हो सकता है, लेकिन मैं मानकीकरण के प्रयास की सराहना करता हूं। यह उस क्षेत्र में संबंधित विधियों के साथ बहुत अधिक समता की अनुमति देता है।

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