2009-08-20 15 views
14

मेरे पास एक तृतीय-पक्ष, बंद स्रोत एप्लिकेशन है जो एक COM इंटरफ़ेस निर्यात करता है, जिसका उपयोग मैं अपने सी # .NET अनुप्रयोग में इंटरऑप के माध्यम से कर रहा हूं। यह COM इंटरफ़ेस कई ऑब्जेक्ट्स निर्यात करता है जो सभी सिस्टम के रूप में दिखाई देते हैं। ऑब्जेक्ट जब तक कि मैं उन्हें उपयुक्त इंटरफ़ेस प्रकार पर नहीं डालता। मैं इन सभी वस्तुओं की एक संपत्ति असाइन करना चाहता हूं। इस प्रकार:"संपत्ति एक्स" बाधा के साथ जेनेरिक फ़ंक्शन?

foreach (object x in BigComInterface.Chickens) 
{ 
    (x as Chicken).attribute = value; 
} 
foreach (object x in BigComInterface.Ducks) 
{ 
    (x as Duck).attribute = value; 
} 

लेकिन संपत्ति बताए अपवाद है जहाँ से मैं ठीक करने के लिए चाहते हैं फेंक (आवेदन विशेष कारण है कि अपरिहार्य हैं के लिए) होने की संभावना है, इसलिए मैं वास्तव में हर एक के आसपास आज़माएं/कैच चाहते हैं। इस प्रकार:

foreach (object x in BigComInterface.Chickens) 
{ 
    SetAttribute<Chicken>(x as Chicken, value); 
} 
foreach (object x in BigComInterface.Ducks) 
{ 
    SetAttribute<Duck>(x as Duck, value); 
} 

void SetAttribute<T>(T x, System.Object value) 
{ 
    try 
    { 
     x.attribute = value; 
    } 
    catch 
    { 
     // handle... 
    } 
} 

समस्या देखें:

foreach (object x in BigComInterface.Chickens) 
{ 
    try 
    { 
     (x as Chicken).attribute = value; 
    } 
    catch (Exception ex) 
    { 
     // handle... 
    } 
} 
foreach (object x in BigComInterface.Ducks) 
{ 
    try 
    { 
     (x as Duck).attribute = value; 
    } 
    catch (Exception ex) 
    { 
     // handle... 
    } 
} 

जाहिर है, यह इतना यह करने के लिए क्लीनर हो सकता है? मेरा x मान किसी भी प्रकार का हो सकता है, इसलिए संकलक को संकलित नहीं कर सकता। संग्रहित करें। चिकन और बतख किसी भी प्रकार के विरासत पेड़ में नहीं हैं और वे पर एक इंटरफ़ेस साझा नहीं करते हैं।। अगर उन्होंने किया, तो मैं उस इंटरफ़ेस के लिए टी पर एक बाधा डाल सकता था। लेकिन चूंकि कक्षा बंद स्रोत है, यह मेरे लिए संभव नहीं है।

मेरी कल्पना में, मैं क्या चाहता हूं, कुछ ऐसी बाधाओं की तरह है जो पर तर्क की आवश्यकता होती है। इस सामग्री पर है, भले ही यह किसी दिए गए इंटरफ़ेस को लागू करता हो। बुद्धि के लिए,

void SetAttribute<T>(T x, System.Object value) where T:hasproperty(attribute) 

मैं यहाँ से क्या करना है यकीन नहीं है अन्य कटौती करने के लिए की तुलना में/चिकन, बतख, गाय, भेड़ से प्रत्येक के लिए इस छोटे से आज़माएं/कैच ब्लॉक, और इतने पर पेस्ट करें।

मेरा प्रश्न है: किसी ऑब्जेक्ट पर एक विशिष्ट संपत्ति का आह्वान करने की इच्छा रखने के इस समस्या के लिए एक अच्छा कामकाज क्या है जब उस संपत्ति को लागू करने वाले इंटरफ़ेस को संकलित समय पर नहीं जाना जा सकता है?

उत्तर

4

दुर्भाग्य से, यह वर्तमान में मुश्किल है। सी # 4 में, गतिशील प्रकार इस के साथ काफी मदद कर सकता है। COM इंटरऑप उन स्थानों में से एक है जो गतिशील वास्तव में चमकता है।

हालांकि, इस बीच, एकमात्र विकल्प जो आपको किसी भी प्रकार की वस्तु रखने की इजाजत देता है, इंटरफेस पर कोई प्रतिबंध नहीं, प्रतिबिंब का उपयोग करने के लिए वापस जाना होगा।

आप "विशेषता" संपत्ति ढूंढने के लिए प्रतिबिंब का उपयोग कर सकते हैं, और रनटाइम पर इसका मान सेट कर सकते हैं।

+0

धन्यवाद, रीड। मैंने प्रतिबिंब तकनीक का प्रयास किया - यह मुझे जो चाहिए वह करता है लेकिन धीमा है। – catfood

+0

चूंकि कोई सामान्य इंटरफ़ेस नहीं है, इसलिए तेज़ vtable- आधारित जेनेरिक इनवॉक करने का कोई तरीका नहीं है - आपको नाम पर गतिशील लुकअप करना है, और यह हमेशा धीमा, प्रतिबिंब या नहीं होता है। –

+0

@ पावेल: हाँ, यह हमेशा धीमा रहता है, लेकिन तकनीकी रूप से, यह एकमात्र तरीका है जो मौजूदा कक्षाओं को बदलने के बिना काम करता है, जब तक सी # 4 बाहर नहीं हो जाता। मैं यह नहीं कह रहा कि यह अच्छा है, बल्कि यह काम करेगा। –

2

दुर्भाग्यवश ऐसा करने का एकमात्र तरीका एक इंटरफेस के साथ प्रकार पैरामीटर को बाधित करना है जो उस संपत्ति को परिभाषित करता है और सभी प्रकारों पर लागू होता है।

चूंकि आपके पास स्रोत नहीं है, इसलिए इसे कार्यान्वित करना असंभव होगा और इस तरह आपको कुछ प्रकार के कामकाज का उपयोग करना होगा। सी # स्थैतिक रूप से टाइप किया गया है और इस तरह आप यहां उपयोग करने के लिए बतख-टाइपिंग के प्रकार का समर्थन नहीं करते हैं। सबसे अच्छी बात जल्द ही आ रही है (सी # 4 में) ऑब्जेक्ट को dynamic के रूप में टाइप करना होगा और निष्पादन समय पर संपत्ति कॉल को हल करना होगा (ध्यान दें कि यह दृष्टिकोण सामान्य नहीं होगा क्योंकि आप dynamic के रूप में सामान्य प्रकार पैरामीटर को बाधित नहीं कर सकते हैं)।

+3

आप प्रतिबिंब के माध्यम से ऐसा कर सकते हैं। –

+0

@Reed - अच्छा बिंदु! –

+0

गतिशील के साथ, आपको सामान्य पैरामीटर की आवश्यकता नहीं है। बस एक विधि का उपयोग करें जो मान को गतिशील के रूप में लेता है, और obj.attribute को सेट करने का प्रयास करें। यह ठीक काम करना चाहिए, बशर्ते "विशेषता" obj पर एक संपत्ति के रूप में मौजूद है। –

6

ठीक है, कैसे humongous अपने अपवाद हैंडलिंग कोड है पर निर्भर करता है (और यह काफी इसलिए हो सकता है अगर मैं गलत नहीं हूँ) निम्नलिखित चाल का उपयोग कर आप मदद कर सकते हैं:

class Chicken 
{ 
    public string attribute { get; set; } 
} 

class Duck 
{ 
    public string attribute { get; set; } 
} 

interface IHasAttribute 
{ 
    string attribute { get; set; } 
} 

class ChickenWrapper : IHasAttribute 
{ 
    private Chicken chick = null; 
    public string attribute 
    { 
     get { return chick.attribute; } 
     set { chick.attribute = value; } 
    } 
    public ChickenWrapper(object chick) 
    { 
     this.chick = chick as Chicken; 
    } 
} 

class DuckWrapper : IHasAttribute 
{ 
    private Duck duck = null; 
    public string attribute 
    { 
     get { return duck.attribute; } 
     set { duck.attribute = value; } 
    } 
    public DuckWrapper(object duck) 
    { 
     this.duck = duck as Duck; 
    } 
} 

void SetAttribute<T>(T x, string value) where T : IHasAttribute 
{ 
    try 
    { 
     x.attribute = value; 
    } 
    catch 
    { 
     // handle... 
    } 
} 
+0

यह अंततः सबसे अच्छा समाधान है। रैपर वर्ग आपको तीसरे पक्ष के कोड लेने और अपने विरासत संबंधों को लागू करने की इजाजत देता है। यह आपको तृतीय पक्ष एपीआई में बदलावों से भी प्रेरित करेगा। – plinth

+1

यह सामान्य रूप से एक अच्छा समाधान है, लेकिन मेरी मूल समस्या यह है कि एक थर्ड-पार्टी बंद-स्रोत एप्लिकेशन मुझे सिस्टम से ऑब्जेक्ट्स में लिपटे इंटरऑप के माध्यम से कॉम से चिकन और बतख सौंप रहा है। जैसा कि आप सुझाव देते हैं, मैं उन्हें चिकनप्रैपर और डकप्रैपर में लपेट सकता हूं, लेकिन ऐसा करने के लिए मुझे * सभी * उपलब्ध वस्तुओं, डक, चिकन, स्वान, गुज़ इत्यादि में डालने का प्रयास करना होगा, और फिर परिणाम लपेटें डकवापर, चिकनवापर, स्वानवापर, या गूसवापर, आदि में यह एक भयानक स्विच स्टेटमेंट होगा। – catfood

+0

असल में, रैपर कन्स्ट्रक्टर को देखें: यह एक ऑब्जेक्ट लेता है और वहां कास्टिंग करता है, इसलिए आप केवल "(चिकन) ओ" –

2

सूखी का उल्लंघन करने की जरूरत नहीं करने के लिए सिद्धांत आप प्रतिबिंब का उपयोग कर सकते हैं। यदि आप वास्तव में जेनेरिक का उपयोग करना चाहते हैं, तो आप प्रत्येक प्रकार की तृतीय पक्ष ऑब्जेक्ट के लिए एक रैपर क्लास का उपयोग कर सकते हैं और रैपर एक इंटरफेस को कार्यान्वित कर सकता है जिसे आप जेनेरिक टाइप तर्क को बाधित करने के लिए उपयोग कर सकते हैं।

इसका एक उदाहरण प्रतिबिंब के साथ कैसे किया जा सकता है इसका एक उदाहरण। हालांकि कोड का परीक्षण नहीं किया जाता है।

void SetAttribute(object chickenDuckOrWhatever, string attributeName, string value) 
    { 
     var typeOfObject = chickenDuckOrWhatever.GetType(); 
     var property = typeOfObject.GetProperty(attributeName); 
     if (property != null) 
     { 
      property.SetValue(chickenDuckOrWhatever, value); 
      return; 
     } 

     //No property with this name was found, fall back to field 
     var field = typeOfObject.GetField(attributeName); 
     if (field == null) throw new Exception("No property or field was found on type '" + typeOfObject.FullName + "' by the name of '" + attributeName + "'."); 
     field.SetValue(chickenDuckOrWhatever, value); 
    } 

आप प्रदर्शन के लिए कोड में तेजी लाने के हैं, तो आप FieldInfo और PropertyInfo chickenDuckOrWhatever के प्रकार के अनुसार कैश और दर्शाती से पहले शब्दकोश से परामर्श पहले सकता है।

टीआईपी: स्ट्रिंग के रूप में attributeName हार्डकोड करने की आवश्यकता नहीं है, तो आप सी # 6 सुविधा nameof का उपयोग कर सकते हैं। उदाहरण के लिए नाम (चिकन.एट्रिब्यूटनाम) की तरह।

+0

"टीआईपी उत्कृष्ट" के बजाय "नया चिकनवापर (ओ)" करेंगे। – catfood

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