2010-03-11 24 views
49

के माध्यम से वैकल्पिक पैरामीटर के साथ विधियों को आमंत्रित करना वैकल्पिक पैरामीटर के साथ सी # 4.0 का उपयोग करके मैंने एक और समस्या में भाग लिया है।प्रतिबिंब

मैं फ़ंक्शन कैसे आमंत्रित करूं (या बल्कि एक निर्माता, मेरे पास ConstructorInfo ऑब्जेक्ट है) जिसके लिए मुझे पता है कि इसे किसी भी पैरामीटर की आवश्यकता नहीं है?

type.GetParameterlessConstructor() 
    .Invoke(BindingFlags.OptionalParamBinding | 
      BindingFlags.InvokeMethod | 
      BindingFlags.CreateInstance, 
      null, 
      new object[0], 
      CultureInfo.InvariantCulture); 

(मैं सिर्फ अलग BindingFlags साथ की कोशिश की है):

यहाँ कोड मैं अब का उपयोग करें।

GetParameterlessConstructor एक कस्टम एक्सटेंशन विधि है जिसे मैंने Type के लिए लिखा था।

उत्तर

106

MSDN के अनुसार, डिफ़ॉल्ट पैरामीटर का उपयोग करने के लिए आप Type.Missing पास करना चाहिए।

यदि आपके कन्स्ट्रक्टर के पास तीन वैकल्पिक तर्क हैं तो खाली ऑब्जेक्ट सरणी पास करने के बजाय आप एक तीन तत्व ऑब्जेक्ट सरणी पारित करेंगे जहां प्रत्येक तत्व का मान Type.Missing है, उदा।

type.GetParameterlessConstructor() 
    .Invoke(BindingFlags.OptionalParamBinding | 
      BindingFlags.InvokeMethod | 
      BindingFlags.CreateInstance, 
      null, 
      new object[] { Type.Missing, Type.Missing, Type.Missing }, 
      CultureInfo.InvariantCulture); 
+3

यह उत्तर वास्तव में सही के रूप में चिह्नित एक से बेहतर है! –

+0

मैं यह प्रमाणित कर सकता हूं कि यह काम करता है। मैं मानता हूं कि ज्यादातर मामलों में यह एक बेहतर समाधान है और शायद इस तरह चिह्नित किया जाना चाहिए। – N8allan

+0

क्या आप परिणामी 'MethodInfo' या' ConstructorInfo' पर बस "Invoke" (बिना पैरामीटर के) को कॉल कर सकते हैं? – Alxandr

22

वैकल्पिक पैरामीटर सामान्य विशेषता द्वारा दर्शाए जाते हैं और संकलक द्वारा नियंत्रित किए जाते हैं।
आईएल पर उनके पास कोई प्रभाव नहीं है (मेटाडाटा ध्वज के अलावा), और प्रतिबिंब द्वारा सीधे समर्थित नहीं हैं (IsOptional और DefaultValue गुणों को छोड़कर)।

यदि आप प्रतिबिंब के साथ वैकल्पिक पैरामीटर का उपयोग करना चाहते हैं, तो आपको अपने डिफ़ॉल्ट मान मैन्युअल रूप से पास करने की आवश्यकता होगी।

+0

धन्यवाद। मैं एक समाधान के साथ आया जो वास्तव में करता है। यह सब सुंदर नहीं दिखता है, लेकिन यह काम करता है। और, IsOptional एकमात्र संपत्ति नहीं है, DefaultValue भी मौजूद है, इसलिए मैं बस सभी डिफ़ॉल्ट संस्करणों में से एक सरणी बना देता हूं। – Alxandr

1
ओपनसोर्स ढांचे के साथ

ImpromptuInterface संस्करण 4 के रूप में आप एक very late bound way में कंस्ट्रक्टर्स आह्वान करने के लिए सी # 4.0 में डीएलआर उपयोग कर सकते हैं और यह नामित/वैकल्पिक तर्क के साथ निर्माताओं की पूरी तरह से अवगत है, इस 4 बार Activator.CreateInstance(Type type, params object[] args) की तुलना में तेजी से चलाता है और आप डॉन ' टी को डिफ़ॉल्ट मानों को प्रतिबिंबित करना होगा।

using ImpromptuInterface; 
using ImpromptuInterface.InvokeExt; 

...

//if all optional and you don't want to call any 
Impromptu.InvokeConstructor(type) 

या

//If you want to call one parameter and need to name it 
Impromptu.InvokeConstructor(type, CultureInfo.InvariantCulture.WithArgumentName("culture")) 
3

मैं बस कुछ कोड जोड़ूंगा ... क्योंकि। कोड सुखद नहीं है, मैं सहमत हूं, लेकिन यह काफी सीधे आगे है। उम्मीद है कि इससे किसी ऐसे व्यक्ति की मदद मिलेगी जो इस पर ठोकर खाती है। यह परीक्षण किया जाता है, हालांकि शायद नहीं के साथ-साथ आप उत्पादन परिवेश में चाहते हो जाएगा:

तर्क आर्ग के साथ obj वस्तु पर विधि methodName कॉलिंग:

public Tuple<bool, object> Evaluate(IScopeContext c, object obj, string methodName, object[] args) 
    { 
     // Get the type of the object 
     var t = obj.GetType(); 
     var argListTypes = args.Select(a => a.GetType()).ToArray(); 

     var funcs = (from m in t.GetMethods() 
        where m.Name == methodName 
        where m.ArgumentListMatches(argListTypes) 
        select m).ToArray(); 

     if (funcs.Length != 1) 
      return new Tuple<bool, object>(false, null); 

     // And invoke the method and see what we can get back. 
     // Optional arguments means we have to fill things in. 
     var method = funcs[0]; 
     object[] allArgs = args; 
     if (method.GetParameters().Length != args.Length) 
     { 
      var defaultArgs = method.GetParameters().Skip(args.Length) 
       .Select(a => a.HasDefaultValue ? a.DefaultValue : null); 
      allArgs = args.Concat(defaultArgs).ToArray(); 
     } 
     var r = funcs[0].Invoke(obj, allArgs); 
     return new Tuple<bool, object>(true, r); 
    } 

और समारोह ArgumentListMatches से नीचे है, जो मूल रूप से लेता है तर्क शायद GetMethod में पाई जाने वाली जगह:

public static bool ArgumentListMatches(this MethodInfo m, Type[] args) 
    { 
     // If there are less arguments, then it just doesn't matter. 
     var pInfo = m.GetParameters(); 
     if (pInfo.Length < args.Length) 
      return false; 

     // Now, check compatibility of the first set of arguments. 
     var commonArgs = args.Zip(pInfo, (margs, pinfo) => Tuple.Create(margs, pinfo.ParameterType)); 
     if (commonArgs.Where(t => !t.Item1.IsAssignableFrom(t.Item2)).Any()) 
      return false; 

     // And make sure the last set of arguments are actually default! 
     return pInfo.Skip(args.Length).All(p => p.IsOptional); 
    } 

LINQ बहुत से, और इस किया गया प्रदर्शन का परीक्षण नहीं किया गया है!

इसके अलावा, यह जेनेरिक फ़ंक्शन या विधि कॉल को संभाल नहीं पाएगा। इससे यह काफी बदसूरत हो जाता है (जैसे GetMethod कॉल दोहराया जाता है)।

1

सभी प्रश्न गायब हो जाते हैं के रूप में आप अपने कोड decompiled देखें:

C#:

public MyClass([Optional, DefaultParameterValue("")]string myOptArg) 

MSIL:

.method public hidebysig specialname rtspecialname instance void .ctor([opt]string myOptArg) cil managed 

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