C # जेनरेशन डेलीगेट टाइप की कमी की अनुमति नहीं देगी


79

क्या सी # में एक वर्ग को परिभाषित करना संभव है ऐसा

class GenericCollection<T> : SomeBaseCollection<T> where T : Delegate

मैं इस जीवन के लिए नहीं कर सका। मैंने प्रयोग करने की कोशिश की

delegate, Delegate, Action<T> and Func<T, T>

यह मुझे लगता है कि यह किसी तरह से स्वीकार्य होना चाहिए। मैं अपने खुद के EventQueue को लागू करने की कोशिश कर रहा हूं।

मैंने इसे समाप्त कर दिया [आदिम सन्निकटन मन तुम]।

internal delegate void DWork();

class EventQueue {
    private Queue<DWork> eventq;
}

लेकिन फिर मैं विभिन्न प्रकार के कार्यों के लिए एक ही परिभाषा का पुन: उपयोग करने की क्षमता खो देता हूं।

विचार?

जवाबों:


66

जेनेरिक विरोधाभासों के रूप में कई कक्षाएं अनुपलब्ध हैं - एनम एक और।

, शायद जाँच करने के लिए (उदाहरण के लिए, स्थिर निर्माता में) प्रतिबिंब का उपयोग कर कि टी: प्रतिनिधियों के लिए, निकटतम आप प्राप्त कर सकते हैं "वर्ग" है एक प्रतिनिधि:

static GenericCollection()
{
    if (!typeof(T).IsSubclassOf(typeof(Delegate)))
    {
        throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
    }
}

8
+1 के लिए: 1) स्टैटिक कंस्ट्रक्टर और 2 का उपयोग करके) विस्तृत डीबगिंग शर्तों के कारण एक विस्तृत संदेश सहित।
सैम हैरवेल

6
@MarcGravell: एक स्थिर इनिशियलाइज़र उल्लंघन में एक अपवाद को फेंकना नहीं है CA1065: Do not raise exceptions in unexpected locations... मैं हमेशा इस धारणा के तहत था कि आपको अपनी कक्षा के अमान्य उपयोगों को खोजने के लिए एक कस्टम कोड विश्लेषण नियम का उपयोग करना चाहिए जो आमतौर पर रन-टाइम पर उपलब्ध नहीं होते हैं।
22

3
C # 7.3 (मई 2018 को जारी किया गया) में शुरू होने पर, इसे इस तरह से विवश करने की अनुमति है where T : Delegate, और (किसी ने इस बारे में एक नया उत्तर पोस्ट किया है)।
जेपी स्टिग नीलसन

16

हां यह C # 7.3 में संभव है, बाधाओं को शामिल करने के लिए परिवार बढ़े Enum, Delegateऔर unmanagedप्रकार। आप इस कोड को बिना किसी समस्या के लिख सकते हैं:

void M<D, E, T>(D d, E e, T* t) where D : Delegate where E : Enum where T : unmanaged
    {

    }

डॉक्स से :

C # 7.3 के साथ शुरुआत करके, आप यह निर्दिष्ट करने के लिए मानवरहित बाधा का उपयोग कर सकते हैं कि टाइप पैरामीटर एक गैर-अशक्त अप्रबंधित प्रकार होना चाहिए। अप्रबंधित बाधा आपको उन प्रकार के साथ काम करने के लिए पुन: प्रयोज्य दिनचर्या लिखने में सक्षम बनाती है जिन्हें स्मृति के ब्लॉक के रूप में हेरफेर किया जा सकता है

उपयोगी कड़ियाँ:

Microsoft बिल्ड 2018 से C # का भविष्य

C # 7.3 में नया क्या है?


हां, यह C # 7.3 (मई 2018 से) संभव है, और आप यहां जारी नोट देख सकते हैं ।
जेपी स्टिग नीलसन

13

संपादित करें: इन लेखों में कुछ प्रस्तावित कार्य-स्थल प्रस्तावित हैं:

http://jacobcarpenters.blogspot.com/2006/06/c-30-and-delegate-conversion.html

http://jacobcarpenters.blogspot.com/2006_11_01_archive.html


से सी # 2.0 विनिर्देश हम (20.7, प्रतिबन्ध) पढ़ सकते हैं:

एक वर्ग-प्रकार की बाधा को निम्नलिखित नियमों को पूरा करना चाहिए:

  • प्रकार एक वर्ग प्रकार होना चाहिए।
  • प्रकार को सील नहीं किया जाना चाहिए।
  • प्रकार निम्न में से एक प्रकार का नहीं होना चाहिए: System.Array, System.Delegate, System.Enum, या System.ValueType
  • प्रकार ऑब्जेक्ट नहीं होना चाहिए। क्योंकि सभी प्रकार की वस्तुएं प्राप्त होती हैं, इस तरह की बाधा का कोई प्रभाव नहीं पड़ता अगर इसे अनुमति दी जाती।
  • किसी दिए गए प्रकार के पैरामीटर के लिए अधिकांश एक बाधा एक वर्ग प्रकार हो सकती है।

और निश्चित रूप से पर्याप्त VS2008 एक त्रुटि से बाहर निकलता है:

error CS0702: Constraint cannot be special class 'System.Delegate'

इस मुद्दे पर जानकारी और जांच के लिए यहां पढ़ें ।


10

यदि आप एक IL Weaver पर एक संकलन समय निर्भरता लेने के लिए तैयार हैं तो आप Fody के साथ ऐसा कर सकते हैं ।

इस ऐड का उपयोग Fody https://github.com/Fody/ExtraConstraints पर करें

आपका कोड इस तरह दिख सकता है

public class Sample
{
    public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
    {        
    }
    public void MethodWithEnumConstraint<[EnumConstraint] T>()
    {
    }
} 

और इसके लिए संकलित किया जाए

public class Sample
{
    public void MethodWithDelegateConstraint<T>() where T: Delegate
    {
    }

    public void MethodWithEnumConstraint<T>() where T: struct, Enum
    {
    }
}

टूटी हुई कड़ी। क्या आपके पास करंट है?
जस्टिन मॉर्गन

3

प्रतिनिधि पहले से ही जंजीरों का समर्थन करता है। क्या यह आपकी जरूरतों को पूरा नहीं करता है?

public class EventQueueTests
{
    public void Test1()
    {
        Action myAction = () => Console.WriteLine("foo");
        myAction += () => Console.WriteLine("bar");

        myAction();
        //foo
        //bar
    }

    public void Test2()
    {
        Action<int> myAction = x => Console.WriteLine("foo {0}", x);
        myAction += x => Console.WriteLine("bar {0}", x);
        myAction(3);
        //foo 3
        //bar 3
    }

    public void Test3()
    {
        Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; };
        myFunc += x => { Console.WriteLine("bar {0}", x); return x + 1; };
        int y = myFunc(3);
        Console.WriteLine(y);

        //foo 3
        //bar 3
        //4
    }

    public void Test4()
    {
        Func<int, int> myFunc = x => { Console.WriteLine("foo {0}", x); return x + 2; };
        Func<int, int> myNextFunc = x => { x = myFunc(x);  Console.WriteLine("bar {0}", x); return x + 1; };
        int y = myNextFunc(3);
        Console.WriteLine(y);

        //foo 3
        //bar 5
        //6
    }

}

नहीं वास्तव में कार्यक्षमता मैं देख रहा हूँ ... मैं अपने सामान्य वर्ग पर एक प्रकार की बाधा बनाने की कोशिश कर रहा था ...
निकोलस Mancuso

3

मैं एक ऐसी स्थिति में आया था जहां मुझे Delegateआंतरिक रूप से निपटने की जरूरत थी लेकिन मैं एक सामान्य बाधा चाहता था। विशेष रूप से, मैं प्रतिबिंब का उपयोग करके एक घटना हैंडलर जोड़ना चाहता था, लेकिन मैं प्रतिनिधि के लिए एक सामान्य तर्क का उपयोग करना चाहता था। नीचे दिए गए कोड नहीं काम करते हैं, के बाद से "हैंडलर" एक प्रकार चर रहा है, और संकलक डाली नहीं होगा करता है Handlerकरने के लिए Delegate:

public void AddHandler<Handler>(Control c, string eventName, Handler d) {
  c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d);
}

हालाँकि, आप एक फ़ंक्शन पास कर सकते हैं जो आपके लिए रूपांतरण करता है। convertएक Handlerतर्क लेता है और एक रिटर्न देता है Delegate:

public void AddHandler<Handler>(Control c, string eventName, 
                  Func<Delegate, Handler> convert, Handler d) {
      c.GetType().GetEvent(eventName).AddEventHandler(c, convert(d));
}

अब कंपाइलर खुश है। विधि को कॉल करना आसान है। उदाहरण के लिए, KeyPressWindows प्रपत्र नियंत्रण पर ईवेंट को संलग्न करना :

AddHandler<KeyEventHandler>(someControl, 
           "KeyPress", 
           (h) => (KeyEventHandler) h,
           SomeControl_KeyPress);

SomeControl_KeyPressईवेंट लक्ष्य कहां है। कुंजी कनवर्टर लैम्ब्डा है - यह कोई काम नहीं करता है, लेकिन यह संकलक को आश्वस्त करता है कि आपने इसे एक वैध प्रतिनिधि दिया था।

(280Z28 से शुरू करें) @ जस्टिन: इसका उपयोग क्यों नहीं करते?

public void AddHandler<Handler>(Control c, string eventName, Handler d) { 
  c.GetType().GetEvent(eventName).AddEventHandler(c, d as Delegate); 
} 

(अंत 280Z28)


1
@ जस्टिन: मैंने आपकी टिप्पणी को अंत में अपनी टिप्पणी डालने के लिए संपादित किया क्योंकि इसमें एक कोड ब्लॉक है।
सैम हैरवेल

2

जैसा कि ऊपर उल्लेख किया गया है, आपके पास एक सामान्य बाधा के रूप में डेलिगेट्स और एनम नहीं हो सकता है। System.Objectऔर System.ValueTypeयह भी एक सामान्य बाधा के रूप में इस्तेमाल नहीं किया जा सकता है।

यदि आप IL में एक उपयुक्त कॉल का निर्माण करते हैं तो आस-पास का काम हो सकता है। यह ठीक काम करेगा।

यहाँ जॉन स्कीट ने एक अच्छा उदाहरण दिया है।

http://code.google.com/p/unconstrained-melody/

मैंने जॉन स्केट की पुस्तक C # से डेथ , 3 संस्करण में अपना संदर्भ लिया है ।


1

MSDN के अनुसार

संकलक त्रुटि CS0702

बाधा विशेष वर्ग की 'पहचानकर्ता' नहीं हो सकती है, निम्नलिखित प्रकारों का उपयोग बाधाओं के रूप में नहीं किया जा सकता है:

  • प्रणाली
  • सिस्टम
  • प्रणाली
  • प्रणाली
  • System.ValueType।

आप यहाँ प्रश्न क्यों दोहराते हैं? तुम हमें कुछ भी नया मत बताओ।
एल्म्यू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.