2017-04-07 3 views
18

एक वर्ग पुस्तकालय में निम्न कोड पर विचार करें का उपयोग कर एक बहस अनुप्रयोगों को नहीं तोड़ता है छोड़:पुस्तकालय में प्रतिनिधि हस्ताक्षर बदलने यह

static void Main(string[] args) 
{ 
    s = new Service(); 
    s.Print(Concat); 
} 

static string Concat(string s1, string s2) 
{ 
    return string.Format("{0}-{1}", s1, s2); 
} 

:

public class Service 
{ 
    public delegate string Formatter(string s1, string s2); 

    public void Print(Formatter f) 
    { 
     Console.WriteLine(f("a", "b")); 
    } 
} 

और यहाँ एक सांत्वना आवेदन यह का उपयोग करता है है अब तक यह "ab" प्रिंट करता है, जैसा कि कोई उम्मीद करेगा।

अब, मैं इस प्रकार वर्ग पुस्तकालय बदलने के लिए:

public class Service 
{ 
    public delegate string Formatter(string s1); 

    public void Print(Formatter f) 
    { 
     Console.WriteLine(f("a")); 
    } 
} 

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

इसके विपरीत, जब मैं ऐप चलाता हूं तो कोई अपवाद नहीं होता है, और मुझे आश्चर्यजनक आउटपुट मिलता है "-a"। जब मैं डीबग करता हूं, तो मैं देख सकता हूं कि कंसैट विधि (2 पैरामीटर के साथ) कहा जाता है, नीचे कॉल स्टैक स्तर एफ ("ए") (एक पैरामीटर) को प्रिंट करने के लिए प्रिंट करता है, कहीं भी त्रुटि संकेत नहीं। सबसे दिलचस्प बात यह है कि कंसैट एस 1 में शून्य है, एस 2 "ए" है।

मैंने हस्ताक्षर में विभिन्न परिवर्तनों के साथ भी खेला (पैरामीटर जोड़ना, पैरामीटर प्रकार बदलना) ज्यादातर एक ही परिणाम के साथ। जब मैंने स्ट्रिंग से int तक s2 के प्रकार को बदल दिया, तो मुझे अपवाद मिला, लेकिन जब कंसट विधि को कॉल नहीं किया गया था, लेकिन जब यह स्ट्रिंग को कॉल करने का प्रयास किया गया। Format।

मैंने इसे .NET लक्ष्य ढांचे 4.5.1 और 3.5, x86 और x64 के साथ करने की कोशिश की।

क्या कोई जवाब दे सकता है कि यह अपेक्षित व्यवहार या बग है या नहीं? यह मेरे लिए बहुत खतरनाक लगता है।

+0

यदि आप एक को हटाने के बजाय पैरामीटर जोड़ते हैं तो क्या होता है? –

+1

हे, हाँ, मैं व्यवहार को दोहरा सकता हूं; वह ... सीएलआर बग की तरह दिखता है? मैं मानता हूं कि यह निश्चित रूप से सहज नहीं है –

+2

* विशेष रूप से * जिज्ञासा यह है कि यह आईएल निरीक्षण भी पास करता है - 'peverify' इसके साथ पूरी तरह से खुश है (हालांकि शायद यह आईएल में नहीं किया जा सकता है, और स्थगित होने की जरूरत है रनटाइम करने के लिए)। अनिवार्य रूप से, प्रतिनिधि कन्स्ट्रक्टर (फ़ंक्शन का प्रतिनिधित्व करने) के लिए 'मूल int' पैरामीटर एक मैच के लिए सत्यापित नहीं है, ऐसा लगता है कि –

उत्तर

5

यहां एक सरल रेपो है - मूल रूप से, मैं गलत हस्ताक्षर के साथ एक विधि लक्ष्य पारित करने के लिए प्रतिनिधि प्रकार (जिसे आईएल उपयोग करता है) पर "हुड के तहत" कन्स्ट्रक्टर का उपयोग कर रहा हूं ... और यह काम करता है ठीक (जिसके द्वारा मैं मतलब है कि यह एक अपवाद फेंक नहीं है - यह सिर्फ अपने कोड की तरह बर्ताव):

using System; 

static class P 
{ 
    static void Main() 
    { 
     // resolve the (object, IntPtr) ctor 
     var ctor = typeof(Func<string, string>).GetConstructors()[0]; 

     // resolve the target method 
     var mHandle = typeof(P).GetMethod(nameof(Concat)) 
      .MethodHandle.GetFunctionPointer(); 
     object target = null; // because: static 

     // create delegate instance 
     var del = (Func<string, string>)ctor.Invoke(new object[] { target, mHandle }); 
     var result = del("abc"); 
     Console.WriteLine(result); // "-abc" 
    } 
    public static string Concat(string s1, string s2) 
    { 
     return string.Format("{0}-{1}", s1, s2); 
    } 
} 

यह वास्तव में एक व्याख्या नहीं है। लेकिन अगर आप किसी और से सीएलआर-विशेषज्ञ से पूछना चाहते हैं तो यह सहायक हो सकता है! मेरे पास होगा प्रतिनिधि कन्स्ट्रक्टर ने लक्ष्य गलत होने के बारे में जोर से शिकायत की है।

अनुमान (शुद्ध अटकलें) पर, यह एक मामला है: यदि आप IntPtr (देशी int) पास कर रहे हैं, तो आप पूरी तरह से अपने आप हैं - कोड सबसे तेज़ चीज संभव है। यह अवांछित के लिए एक बुरा जाल की तरह प्रतीत होता है, यद्यपि!

क्यों s2 मूल्य नहीं है और s1 खाली है का सवाल है: मैं है कि लगता है कि क्योंकि ढेर बनाता नीचे (नहीं), इसलिए एक दो पैरामीटर विधि में, arg1 पैरामीटर तुरंत के निकट है ढेर पर पिछली स्थिति। जब हम दो के बजाय एक मान पास करते हैं, तो हम केवल एक मान डालते हैं, इसलिए s2 का मान है, और s1 अपरिभाषित है (पिछले कोड से कचरा हो सकता है)।

+0

एस 1 और एस 2 के मूल्यों के संबंध में मुझे वही अनुमान था। हालांकि पैरामीटर एस 3 जोड़ने के समान परिणाम भी हैं (एस 1 शून्य है और एस 2 "ए" है), जबकि स्टैक लेआउट स्पष्टीकरण के साथ तीन पारित पैरामीटर में से दो को चुना जाना चाहिए। – dzs

+0

दिलचस्प है कि कंसट में एक और पैरामीटर (एस 3) जोड़ना (और स्ट्रिंग.फॉर्मैट में इसका उपयोग करना) AccessViolationException में परिणाम देता है। – Evk

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