2009-08-06 14 views
7

क्या कोई बता सकता है कि निम्नलिखित दो उदाहरणों में से पहला वैध क्यों है और दूसरा नहीं है? अधिक विशेष रूप से, पहले उदाहरण में टी और टीप्रोपर्टी के बीच एक रिश्ता कैसे बनाया गया है?कक्षा संपत्ति के लिए लैम्ब्डा अभिव्यक्ति

//Example 1 
class SomeClass<T> where T : class 
{ 
    void SomeMethod<TProperty>(Expression<Func<T,TProperty>> expression){ ... } 
} 

//Example 2 
class SomeClass 
{ 
    void SomeMethod<T,TProperty>(Expression<Func<T,TProperty>> expression) 
     where T : class{ ... } 
} 

दो उदाहरणों को देखते हुए मैं उम्मीद करता हूं कि निम्नलिखित कार्यान्वयन काम करेंगे, लेकिन दूसरा नहीं।

//Implementation w/ Example 1 
var sc = new SomeClass<MyDateClass>(); 
sc.SomeMethod(dt => dt.Year); 

//Implementation w/ Example 2 
var sc = new SomeClass(); 
sc.SomeMethod<MyDateClass>(dt => dt.Year); 

क्या मैं कठिनाई मेरे मन लपेटकर चारों ओर हो रही है जब SomeMethod क्रियान्वित कैसे पहला उदाहरण/कार्यान्वयन TProperty सामान्य प्रकार उपेक्षा कर सकते हैं, फिर भी दूसरे उदाहरण/कार्यान्वयन नहीं कर सकते हैं और कैसे एक अंतर्निहित संबंध लगता है उदाहरण 2 में विधि की 1.

समाधान हस्ताक्षर बदलें उदाहरण/कार्यान्वयन में टी और TProperty के बीच स्थापित किया इस प्रकार है:

void SomeMethod<T>(Expression<Func<T,Object>> expression){ ... } 

हालांकि यह अभिव्यक्ति में मनमानी वस्तुओं का उपयोग करने की अनुमति देगा, जिसके परिणामस्वरूप यह कार्यान्वयन में वर्णित संपत्ति अभिव्यक्ति की अनुमति देता है।

उत्तर

7

आप कंपाइलर को प्रकार पैरामीटर का अनुमान लगाने की अनुमति दे रहे हैं। इस मामले में 1 के लिए ठीक काम करता है:

class SomeClass<T> where T : class { 
    void SomeMethod<TProperty>(Expression<Func<T,TProperty>> expression){ ... } 
} 

क्योंकि आप पहली बार एक उदाहरण की घोषणा:

var sc = new SomeClass<MyDateClass>(); 

जो संकलक कि T is MyDateClass बताता है। फिर, जब आप विधि कॉल:

sc.SomeMethod(dt => dt.Year); 

संकलक जानता है कि T is MyDateClass, तो T.Year किसी पूर्णांक होना चाहिए। SomeMethod को SomeMethod<MyDateClass, int> के रूप में हल करने के लिए पर्याप्त जानकारी है।

आपका दूसरे उदाहरण है, तथापि, स्पष्ट रूप से प्रकार पैरामीटर (रों) बताते हुए है - को संकलक बता यह अनुमान नहीं लगा:

sc.SomeMethod<MyDateClass>(dt => dt.Year); 

दुर्भाग्य से, यह केवल एक ही प्रकार के साथ SomeMethod कॉल करने के लिए कोशिश कर रहा है पैरामीटर (<MyDateClass> बिट)। चूंकि यह अस्तित्व में नहीं है, यह शिकायत करेगा और कहेंगे कि आपको 2 प्रकार के पैरामीटर की आवश्यकता है।

, तो इसके बजाय, आप इसे कॉल करने के लिए थे, जैसे आप पहले उदाहरण किया:

sc.SomeMethod(dt => dt.Year); 

संकलक, शिकायत करेगा जो आपको बताएगा कि यह प्रकार पैरामीटर अनुमान नहीं लगा सकता - वहाँ बस नहीं है क्या यह निर्धारित करने के लिए पर्याप्त जानकारी dt है। तो, आप स्पष्ट रूप से यह कर सकता है दोनों प्रकार पैरामीटर:

sc.SomeMethod<MyDateClass, int>(dt => dt.Year); 

या, संकलक है क्या dt बता (राशि है जो आप Newing SomeClass<MyDateClass> से पहले उदाहरण में किया था):

sc.SomeMethod((MyDateClass dt) => dt.Year); 

संपादित करता और अपडेट:

तो प्रभावी ढंग से संकलक पहले उदाहरण में जादू कर रहा है? एक धारणा बनाना कि टीप्रोपर्टी एक int होना चाहिए क्योंकि। एक int int है? यह मेरे प्रश्न का उत्तर देगा, यह नहीं कह सकता कि यह संतोषजनक है।

वास्तव में जादू नहीं है, लेकिन छोटे चरणों की एक श्रृंखला है।illustate करने के लिए:

  1. sc प्रकार SomeClass<MyDateClass> की है, T = MyDateClass
  2. SomeMethod बनाने dt => dt.Year की एक पैरामीटर के साथ कहा जाता है।
  3. dt होना चाहिए एक टी (SomeMethod कैसे परिभाषित किया जाता है कि) है, जो होना चाहिए उदाहरण sc के लिए एक MyDateClass
  4. dt.Year एक int होना चाहिए, क्योंकि MyDateClass.Year एक int घोषित किया गया है।
  5. dt => dt.Year के बाद से हमेशा किसी पूर्णांक, expressionकी वापसी वापसी पूर्णांक होना चाहिए होगा। इसलिए TProperty = int
  6. यह पैरामीटर expression से SomeMethod, Expression<Func<MyDateClass,int>> बनाता है। याद रखें कि T = MyDateClass (चरण 1), और TProperty = int (चरण 5)।
  7. अब हमने SomeMethod<T, TProperty> = SomeMethod<MyDateClass, int> बनाने के सभी प्रकार के पैरामीटर का पता लगाया है।

[बी] ut क्या श्रेणी स्तर पर टी निर्दिष्ट करने और विधि के स्तर पर निर्दिष्ट करने के बीच का अंतर है? SomeClass<SomeType> बनाम SomeMethod<SomeType> ... दोनों मामलों में टी ज्ञात है ना?

हाँ, T दोनों मामलों में जाना जाता है। समस्या यह है कि SomeMethod<SomeType> केवल एक ही प्रकार के पैरामीटर के साथ एक विधि कॉल करता है। उदाहरण 2 में, प्रकार पैरामीटर हैं। आप या तो संकलक infer सभी विधि के लिए पैरामीटर पैरामीटर या उनमें से नहीं कर सकते हैं। आप केवल 1 पास नहीं कर सकते हैं और इसे दूसरे (TProperty) का अनुमान लगा सकते हैं। इसलिए, यदि आप स्पष्ट रूप से T बताएंगे, तो आपको स्पष्ट रूप से TProperty भी अवश्य बता देना चाहिए।

+1

तो प्रभावी ढंग से संकलक पहले उदाहरण में जादू कर रहा है? एक धारणा बनाना कि टीप्रोपर्टी एक int होना चाहिए क्योंकि। एक int int है? यह मेरे प्रश्न का उत्तर देगा, यह नहीं कह सकता कि यह संतोषजनक है। :) –

+0

... मुझे लगता है कि प्रकाश बस चला गया। कक्षा और विधि के बीच अंतर यह है कि कक्षा का तात्पर्य है कि टी का एक उदाहरण संदर्भ में मौजूद है जबकि विधि नहीं है? इसलिए संकलक द्वारा धारणाएं? –

+0

सुनिश्चित नहीं है कि आप संतुष्ट क्यों नहीं हैं, लेकिन स्थिर प्रकार का संदर्भ कोई जादू नहीं है, और इसमें स्पष्ट नियम हैं और सी # में एक नई सुविधा है। –

0

समस्या, जहां तक ​​मैं देख सकता हूं, बस DateTime नहीं है कक्षा ... आप T में DateTime (चाहे यह निहित या स्पष्ट है) में गुजर रहे हैं।

यह शायद थोड़ा उलझन में है कि पहले उदाहरण में आपके पास दो प्रकार के पैरामीटर हैं जिन्हें T कहा जाता है - एक प्रकार पर, और विधि पर एक। वे वास्तव में पूरी तरह से अलग कर रहे हैं ..., एक ही हो पहला उदाहरण को फिर से लिखने के रूप में:

//Example 1 
class SomeClass<T> where T : class 
{ 
    void SomeMethod<TProperty>(Expression<Func<T,TProperty>> expression){ ... } 
} 

यह अब एक ही T : class

+0

क्षमा करें बुरा उदाहरण ... जाहिर है डेटटाइम एक संरचना है। आशा है कि समस्या को स्पष्ट रूप से स्पष्ट रूप से स्पष्ट करने के लिए मेरे उदाहरण में संपादन किए गए हैं। ऑब्जेक्ट मानना ​​वास्तव में गुणों वाला एक वर्ग है और संरचना नहीं है। –

+1

हालांकि यह सच है, और यह निश्चित रूप से इस तरह लिखा जा सकता है - मुझे दिलचस्पी है कि कनेक्शन कहां बनाया गया है कि टीप्रोपर्टी वास्तव में टी से संबंधित है, इस प्रकार अभिव्यक्ति में पी => पी। प्रॉपर्टी को जेनेरिक प्रकार को परिभाषित किए बिना अभिव्यक्ति में अनुमति देता है TProperty। सबसे महत्वपूर्ण बात यह है कि मैं एक ही चीज़ को एक विधि के साथ कैसे पूरा करता हूं जो सामान्य वर्ग में नहीं है। क्या मैं समझ रहा हूँ? :) –

1

सबसे पहले है, अपना पहला उदाहरण गलत है, और नहीं होगा दिए गए संकलन करें। विधि पर public गुम होने के अलावा, आप T को दो बार परिभाषित करते हैं - कक्षा में एक बार, और एक बार विधि पर। यह किसी और में त्रुटि नहीं है (हालांकि आपको चेतावनी मिलेगी), लेकिन विधि कॉल को संकलित करते समय, आपको वही त्रुटि मिल जाएगी जैसा कि आप उदाहरण # 2 के लिए प्राप्त करने का वर्णन करते हैं। तो, मुझे लगता है वास्तविक कोड इस तरह है:

//Example 1 
class SomeClass<T> where T : class 
{ 
    public void SomeMethod<TProperty>(Expression<Func<T,TProperty>> expression) {} 
} 

इसके अलावा, दोनों अपने उदाहरण "अनदेखा" (जो है, उसका अनुमान लगा) TProperty के वास्तविक प्रकार SomeMethod करने के लिए अपने कॉल में। आपका दूसरा उदाहरण स्पष्ट रूप से T के प्रकार को निर्दिष्ट करता है, हां, लेकिन TProperty नहीं।

पहले उदाहरण में स्थापित कोई अंतर्निहित संबंध नहीं है, या तो। जब आप T=MyDateClass साथ SomeClass<> का दृष्टांत, कि इन्स्टेन्शियशन के लिए SomeMethod के हस्ताक्षर प्रभावी रूप से हो जाता है:

void SomeMethod<TProperty>(Expression<Func<MyDateClass, TProperty>> expression) 

तो लैम्ब्डा का तर्क प्रकार इस बिंदु पर जाना जाता है, तो आप इसे निर्दिष्ट करने के लिए नहीं है। अभिव्यक्ति का वापसी प्रकार लैम्ब्डा => के दाईं ओर अभिव्यक्ति के प्रकार के रूप में अनुमानित है, और यह TProperty का अनुमानित प्रकार होगा।

दूसरे उदाहरण में, T को कक्षा को तुरंत चालू करते समय स्पष्ट रूप से निर्दिष्ट नहीं किया गया था, और चूंकि विधि तर्क से इसका अनुमान लगाने का कोई तरीका नहीं है, इसलिए इसे स्पष्ट रूप से निर्दिष्ट किया जाना चाहिए। एक बार जब आप इसे निर्दिष्ट कर लेंगे, तो TProperty सटीक रूप से अनुमानित है जैसा कि उदाहरण # 1 के लिए है।

+0

मुझे समझ में नहीं आता कि आप अपनी निडर टिप्पणी के साथ क्या कह रहे हैं। क्या आप कह रहे हैं कि न तो उदाहरण काम नहीं करना चाहिए क्योंकि उदाहरणों में टीप्रोपर्टी का प्रकार अज्ञात है? वाक्यविन्यास त्रुटियों के बारे में, हां, आरोप के रूप में दोषी - मैंने पोस्ट करने से पहले इन उदाहरणों को संकलित करने का प्रयास नहीं किया था। –

+0

"इन्फर" का अर्थ है "स्वचालित रूप से प्रकार को समझें"। इस मामले में यह इस प्रकार है: 'टी' का प्रकार ज्ञात है ->' टी 1' 'फन ' में ज्ञात है -> 'dt' का प्रकार ज्ञात है -> लैम्बडा का रिटर्न प्रकार अभिव्यक्ति के प्रकार के रूप में अनुमानित है .Year' (एक बार जब हम 'dt' के प्रकार को जानते हैं, तो हम स्पष्ट रूप से' dt.Year' -> 'TRESSult' 'Func ' में ज्ञात हैं -> 'टीप्रोपर्टी' ज्ञात है। –

+0

भ्रम नहीं है समझने के साधनों की समझ के साथ, लेकिन विधि स्तर पर वर्ग स्तर बनाम MyDateClass को परिभाषित करने में एक अंतर आता है।अर्थात; SomeClass , कुछ विधि ... इन दोनों मामलों में टी 1 ज्ञात है तो क्यों टीपीप्रर्टी का प्रकार वह पहला उदाहरण है, लेकिन दूसरा नहीं? –

1

विनिर्देशों को टाइप करने के बजाय, कृपया Eric Lippert द्वारा इस पोस्ट को देखें। मुझे लगता है कि यह जवाब देगा कि आप क्या पूछने का प्रयास कर रहे हैं।

मुझे एहसास है कि यह लंबा और बढ़िया है, लेकिन मुझे लगता है कि यह आपको जवाब देने के लिए हिरण के लिए सबसे अच्छा बैंग है।

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