2012-02-23 12 views
11

के रूप में एक सामान्य फ़ंक्शन पास करना मुझे पता है कि मैं जो कर रहा हूं वह अलग तरीके से किया जा सकता है, लेकिन मैं उत्सुक हूं कि चीजें कैसे काम करती हैं। निम्नलिखित एक सरलीकृत कोड है जो संकलित नहीं करता है, लेकिन यह मेरा लक्ष्य दिखाना चाहिए।पैरामीटर

private void Execute() 
{ 
    GeneralizedFunction("1", "2", i => Transform(i)); 
} 

void GeneralizedFunction(string aStringA, string aStringB, Func<string, T> aAction) 
{ 
    A result1 = aAction(aStringA); 
    B result2 = aAction(aStringB); 
    // Do something with A and B here 
} 

T Transform<T>(string aString) 
{ 
    return default(T); 
} 

Transform किसी वस्तु को स्ट्रिंग से एक सामान्य रूपांतरण है (लगता है अक्रमांकन)। GeneralizedFunction ट्रांसफॉर्म के दो विशेषज्ञताओं का उपयोग करता है: एक प्रकार के लिए ए और एक प्रकार बी के लिए। मुझे पता है कि मैं इसे कई अन्य तरीकों से कर सकता हूं (ऑब्जेक्ट के प्रकार के लिए पैरामीटर पेश करके कहें), लेकिन मैं देख रहा हूं जेनेरिक/लैम्बडास के साथ ऐसा करना संभव है या असंभव है या नहीं। यदि सामान्यीकृत फ़ंक्शन के पैरामीटर के रूप में पारित होने से पहले ट्रांसफॉर्म विशिष्ट है, तो यह असंभव है। फिर सवाल यह है कि यह संभावना प्रतिबंधित क्यों है।

+0

यह क्या है: अपने विशिष्ट उपयोग के लिए

interface IGenericFunc { TResult Call<TArg,TResult>(TArg arg); } // ... in some class: void Test(IGenericFunc genericFunc) { // for example's sake only: int x = genericFunc.Call<String, int>("string"); object y = genericFunc.Call<double, object>(2.3); } 

, यह करने के लिए सरल किया जा सकता आप बिल्कुल करना चाहते हैं? चूंकि आप ट्रांसफॉर्म फ़ंक्शन के संबंध में सामान्यीकृत फ़ंक्शन को किसी भी प्रकार की जानकारी देना नहीं चाहते हैं, फिर एक स्ट्रिंग लेने और किसी ऑब्जेक्ट को वापस करने के लिए फ़ंक्शन को स्वीकार क्यों न करें (जिसमें से सभी जानते हैं कि हर कोई *) – Polity

+0

बात यह है कि, "कुछ करें ए और बी "प्लेसहोल्डर समस्याग्रस्त हिस्सा छुपाता है। क्या ए और बी हमेशा विशेष प्रकार के होने जा रहे हैं? तब आपको जेनेरिक की आवश्यकता नहीं है। क्या वे मनमाने ढंग से (शायद बाधाओं के साथ) प्रकार हैं? फिर 'सामान्यीकृत समारोह' में सामान्य होना आवश्यक है। – AakashM

+0

ए और बी ठोस प्रकार हैं, लेकिन ट्रांसफॉर्म एक सामान्य कार्य है। – Max

उत्तर

1

निम्नलिखित हस्ताक्षर का प्रयास करें:

void GeneralizedFunction<T>(string aStringA, string aStringB, Func<string, T> aAction) 

(ध्यान दें कि GeneralizedFunction सामान्य हो गया है, जब विधि बुला संकलक प्रकार पैरामीटर लगता है कि स्वचालित रूप से होगा)।

+0

मैंने कोशिश की। समस्या यह है कि आप टी के साथ दोनों प्रकार ए और बी को संदर्भित करने का प्रयास कर रहे हैं। समारोह घोषणा से को निकालने का मेरा इरादा था। – Max

+0

फिर आपको ए और बी दोनों को टी – Matthias

+0

के साथ प्रतिस्थापित करना होगा, मुझे सादगी के लिए सामान्यीकृत फ़ंक्शन के अंदर ऑब्जेक्ट्स पर विशिष्ट संचालन करने की आवश्यकता है। मैंने अपने नमूना कोड में कोई विवरण नहीं लिखा है, लेकिन लिखे गए सब कुछ आवश्यक है। – Max

0
void GeneralizedFunction<T>(string aStringA, string aStringB, Func<string, T> aAction) 
{ 
    A result1 = aAction(aStringA); 
    B result2 = aAction(aStringB); 
} 

T Transform<T>(string aString) 
{ 
    return default(T); 
} 
4

जो आप करना चाहते हैं वह अकेले जेनेरिक का उपयोग करना संभव नहीं है। कंपाइलर को आपके Transform फ़ंक्शन के दो टाइप किए गए संस्करण जेनरेट करने की आवश्यकता है: A टाइप करने के लिए एक और B टाइप करने के लिए एक। कंपाइलर को संकलन समय पर इसे उत्पन्न करने के बारे में जानने का कोई तरीका नहीं है; केवल कोड चलाकर यह पता चलेगा कि ए और बी की आवश्यकता है। इसे हल करने

एक तरह से दो संस्करणों में पारित करने के लिए होगा:

private void Execute() 
{ 
    GeneralizedFunction("1", "2", i => Transform<A>(i), i => Transform<B>(i)); 
} 

void GeneralizedFunction(string aStringA, string aStringB, Func<string, A> aAction, Func<string, B> bAction) 
{ 
    A result1 = aAction(aStringA); 
    B result2 = bAction(aStringB); 
} 

संकलक जानता है कि वास्तव में क्या यह इस मामले में उत्पन्न करने के लिए की जरूरत है।

+0

हां, मुझे पता है कि मैं दो उदाहरण पारित कर सकता हूं, लेकिन मुझे उम्मीद थी कि सामान्य कार्य (इसलिए मेरे प्रश्न का शीर्षक) पारित करना संभव है और सामान्यीकृत समारोह के अंदर उस सामान्य कार्य के दो विशेषज्ञताओं को बनाना संभव है। – Max

+0

मुझे पता है कि कोड वह नहीं है जो आप चाहते हैं। आपका सवाल यह था कि संभावना प्रतिबंधित क्यों है। उम्मीद है कि अब आप समझते हैं कि आप जो करना चाहते हैं वह कंपाइलर के साथ काम नहीं करेगा। –

+0

यदि मैं ट्रांसफॉर्म फ़ंक्शन के लिए जेनरेट किए गए आईएल कोड को देखता हूं तो ऐसा लगता है कि इसका केवल एक संस्करण है। यहां तक ​​कि जब मैं इसे वस्तुओं के दो वर्गों पर लागू करता हूं। तो ऐसा लगता है कि जेनेरिक फ़ंक्शन का विशेषज्ञता रनटाइम पर किया जाता है। यही है ना – Max

1

ऐसा लगता है कि उत्तर "नहीं" है। की तरह आप के लिए, आप निर्दिष्ट करने की आवश्यकता होगी चाहते हैं, तो आप एक अधूरे निर्मित सामान्य समारोह गुजर सकता है तो परिकल्पित

int i = Transform<int>(""); 

,:

जब आप Transform सीधे कॉल, आप एक प्रकार पैरामीटर निर्दिष्ट करना साथ ही मानकों टाइप करें:

void GeneralizedFunction(string aStringA, string aStringB, Func<string, T> aAction) 
{ 
    A result1 = aAction<A>(aStringA); 
    B result2 = aAction<B>(aStringB); 
    // Do something with A and B here 
} 

तो मुझे लगता है कि आप परिकल्पित ऐसा कर सकता है, अगर सी # ऐसे ही एक वाक्य रचना की थी।

लेकिन उपयोग का मामला क्या है? तारों को मनमाना प्रकार के डिफ़ॉल्ट मान में बदलने के अलावा, मुझे इसके लिए अधिक उपयोग नहीं दिख रहा है। आप एक ऐसे फ़ंक्शन को कैसे परिभाषित कर सकते हैं जो बयान की एक ही श्रृंखला का उपयोग करके दो अलग-अलग प्रकारों में से एक अर्थपूर्ण परिणाम प्रदान करेगा?

संपादित

कारण है कि यह संभव नहीं है के विश्लेषण:

आप अपने कोड में एक लैम्ब्डा अभिव्यक्ति का उपयोग करते हैं, यह या तो एक प्रतिनिधि या एक अभिव्यक्ति पेड़ में संकलित किया गया है; इस मामले में, यह एक प्रतिनिधि है। आपके पास "खुले" जेनेरिक प्रकार का उदाहरण नहीं हो सकता है; दूसरे शब्दों में, एक सामान्य प्रकार से ऑब्जेक्ट बनाने के लिए, सभी प्रकार पैरामीटर निर्दिष्ट किए जाने चाहिए।दूसरे शब्दों में, इसके सभी प्रकार के पैरामीटर के लिए तर्क प्रदान किये बिना किसी प्रतिनिधि का उदाहरण रखने का कोई तरीका नहीं है।

सी # कंपाइलर की सहायक सुविधाओं में से एक अंतर्निहित विधि समूह रूपांतरण है, जहां एक विधि का नाम (एक "विधि समूह") को उस विधि के अधिभारों में से एक का प्रतिनिधित्व करने वाले प्रतिनिधि प्रकार में निहित रूप से परिवर्तित किया जा सकता है। इसी तरह, संकलक एक लैम्ब्डा अभिव्यक्ति को एक प्रतिनिधि प्रकार में निहित रूप से परिवर्तित करता है। दोनों मामलों में, संकलक प्रतिनिधि प्रकार का उदाहरण बनाने के लिए कोड उत्सर्जित करता है (इस मामले में, इसे फ़ंक्शन में पास करने के लिए)। लेकिन उस प्रतिनिधि प्रकार के उदाहरण को अभी भी इसके प्रत्येक प्रकार के पैरामीटर के लिए एक प्रकार का तर्क होना चाहिए।

एक सामान्य समारोह के रूप में सामान्य समारोह पास करने के लिए, ऐसा लगता है, संकलक रूपांतरण बिना विधि को विधि समूह या लैम्ब्डा अभिव्यक्ति पारित करने में सक्षम होने की जरूरत है, इसलिए aAction पैरामीटर किसी भी प्रकार का "विधि समूह" या "लैम्ब्डा अभिव्यक्ति" होगा। फिर, एक प्रतिनिधि प्रकार के लिए निहित रूपांतरण कॉल साइट A result1 = aAction<A>(aStringA); और B result2 = aAction<B>(aStringB); पर हो सकता है। बेशक, इस बिंदु पर, हम contrafactuals और hypotheticals के ब्रह्मांड में अच्छी तरह से हैं।

समाधान मैं के साथ खत्म हो गया दोपहर के भोजन के इस था, एक समारोह Deserialize<T> कि एक स्ट्रिंग धारावाहिक डेटा वाली लेता है और प्रकार T की एक वस्तु देता है यह सोचते हैं आया:

void GeneralizedFunction<T>(string aStringA, string aStringB, Func<T, string> stringGetter) 
{ 
    A result1 = Deserialize<A>(stringGetter(aStringA)); 
    B result2 = Deserialize<B>(stringGetter(aStringB)); 
} 

void Example(string serializedA, string serializedB, string pathToA, string pathToB, FileInfo a, FileInfo b) 
{ 
    GeneralizedFunction(serializedA, serializedB, s => s); 
    GeneralizedFunction(pathToA, pathToB, File.ReadAllText); 
    GeneralizedFunction(a, b, fi => File.ReadAllText(fi.FullName)); 
} 
+0

एक उपयोग केस deserialization है। स्ट्रिंग एक ऑब्जेक्ट का प्रतिनिधित्व है और ट्रांसफॉर्म उस ऑब्जेक्ट का एक स्ट्रिंग प्रस्तुतीकरण – Max

+0

@ मैक्स से एक उदाहरण बनाता है लेकिन इसे सीधे कॉल करने के बजाय 'सामान्यीकृत फ़ंक्शन 'में' ट्रांसफॉर्म 'को पास करने के लिए उपयोग केस क्या है? वैसे भी, एक सामान्य 'टी ट्रांसफॉर्म (स्ट्रिंग) नहीं होगा' ऑब्जेक्ट Deserialize (टाइप, स्ट्रिंग) 'जैसे' टी ट्रांसफॉर्म (स्ट्रिंग एस) {वापसी (टी) Deserialize (टाइपोफ (टी) , एस); } ' – phoog

+0

उदाहरण के लिए ट्रांसफॉर्म 1 किसी स्ट्रिंग को किसी ऑब्जेक्ट में परिवर्तित कर सकता है, ट्रांसफॉर्म 2 किसी फ़ाइल को कनवर्ट कर सकता है जो स्ट्रिंग को ऑब्जेक्ट – Max

4

इस उत्तर कारण की व्याख्या नहीं करता क्यों, बस सीमा के चारों ओर काम करने के लिए। एक वास्तविक समारोह में उत्तीर्ण होने की

इसके बजाय, आप एक वस्तु इस तरह के एक समारोह है कि पारित कर सकते हैं:

interface IDeserializerFunc 
{ 
    T Call<T>(string arg); 
} 

// ... in some class: 
void Test(IDeserializerFunc deserializer) 
{ 
    int x = deserializer.Call<int>("3"); 
    double y = deserializer.Call<double>("3.2"); 
}