2012-04-11 20 views
8

में मैं एक सामान्य कार्रवाई प्रतिनिधि बनाने के लिए कोशिश कर रहा हूँcontravariance भाव

delegate void ActionPredicate<in T1, in T2>(T1 t1, T2 t2); 

और

public static ActionPredicate<T,string> GetSetterAction<T>(string fieldName) 
    { 

     ParameterExpression targetExpr = Expression.Parameter(typeof(T), "Target"); 
     MemberExpression fieldExpr = Expression.Property(targetExpr, fieldName); 
     ParameterExpression valueExpr = Expression.Parameter(typeof(string), "value"); 

     MethodCallExpression convertExpr = Expression.Call(typeof(Convert), "ChangeType", null, valueExpr, Expression.Constant(fieldExpr.Type)); 

     UnaryExpression valueCast = Expression.Convert(convertExpr, fieldExpr.Type); 
     BinaryExpression assignExpr = Expression.Assign(fieldExpr, valueCast); 
     var result = Expression.Lambda<ActionPredicate<T, string>>(assignExpr, targetExpr, valueExpr); 
     return result.Compile(); 
    } 

और यहां मेरे फोन करने वाले

ActionPredicate<busBase, string> act = DelegateGenerator.GetSetterAction<busPerson>("FirstName"); 

है और यहाँ व्यापार वस्तु

है
public abstract class busBase 
{ 

} 
public class busPerson : busBase 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public int Age { get; set; } 

    public string GetFullName() 
    { 
     return string.Format("{0} {1}", FirstName, LastName); 
    } 
} 

और यहाँ त्रुटि मैं क्या

Cannot implicitly convert type 'BusinessObjects.ActionPredicate<BusinessObjects.busPerson,string>' to 'BusinessObjects.ActionPredicate<BusinessObjects.busBase,string>'. An explicit conversion exists (are you missing a cast?)  

मेरे GetSetterAction ActionPerdicate जहां यहाँ के रूप में टी busPerson है लौटा रहा है और मैं में ActionPredicate contravariance के बारे में ध्यान में रखते हुए यह स्टोर करने के लिए कोशिश कर रहा हूँ संकलन के दौरान प्राप्त है। लेकिन यह विफल रहता है। मुझे नहीं पता कि आगे कैसे आगे बढ़ना है। कृपया सहायता कीजिए..!

+0

क्या आपने स्पष्ट रूपांतरण की कोशिश की? – McGarnagle

उत्तर

8

जेनेरिक contravariance करता नहीं (यहाँ Action<T1> उपयोग करते हुए) आप D<TBase> कारण नीचे बताया गया है की वजह से एक प्रतिनिधि के लिए एक प्रतिनिधि D<TDerived> आवंटित करने के लिए अनुमति देते हैं:

Action<string> m1 = MyMethod; //some method to call 
Action<object> m2 = m1; //compiler error - but pretend it's not. 
object obj = new object(); 

m2(obj); //runtime error - not type safe 

आप देख सकते हैं, हम की अनुमति दी गई है, तो इस असाइनमेंट को करें, फिर हम टाइप-सुरक्षा तोड़ेंगे क्योंकि हम m1 को object पास करके और string के उदाहरण के द्वारा प्रतिनिधि m1 को आज़माने और आमंत्रित करने में सक्षम होंगे। दूसरी तरफ जा रहे हैं, यानी, एक प्रतिनिधि संदर्भ को एक प्रकार के प्रतिलिपि बनाना जिसका पैरामीटर प्रकार स्रोत से अधिक व्युत्पन्न है ठीक है। MSDN has a more complete example of generic co/contra variance

इसलिए आप या तो, ActionPredicate<busPerson, string> act या अधिक होने की संभावना को act की घोषणा बदल GetSetterAction विधि लेखन हमेशा ActionPredicate<busBase, string> वापस जाने के लिए विचार करना होगा। आप ऐसा करते हैं, तो आप भी विधि के प्रकार बाधा

where T1 : busBase 

जोड़ना चाहिए, और आप भी इस प्रकार परिवर्तित करने का तरीका आपकी अभिव्यक्ति बनाया गया है, पहले दो लाइनों को बदलने की आवश्यकता होगी:

ParameterExpression targetExpr = Expression.Parameter(typeof(busBase), "Target"); 
//generate a strongly-typed downcast to the derived type from busBase and 
//use that as the type on which the property is to be written 
MemberExpression fieldExpr = Expression.Property(
    Expression.Convert(targetExpr, typeof(T1)), fieldName); 

जेनेरिक बाधा जोड़ना यह सुनिश्चित करने के लिए एक अच्छा स्पर्श है कि यह डाउनकास्ट हमेशा किसी भी T1 के लिए मान्य होगा।

थोड़ा अलग नोट पर - Action<T1, T2> प्रतिनिधि के साथ क्या गलत था? ऐसा लगता है कि आप बिल्कुल वही काम करते हैं? :)

+0

जैसा कि आपने सुझाव दिया था, मैंने एक्शन <बसबेस, स्ट्रिंग> को वापस करने के लिए गीतसेटरएक्शन बदलने की कोशिश की लेकिन मैं इस त्रुटि के साथ समाप्त हुआ "पैरामीटर एक्स्पेरियन ऑफ टाइप 'BusinessObjects.busPerson' का उपयोग 'BusinessObjects.busBase' प्रकार के प्रतिनिधि पैरामीटर के लिए नहीं किया जा सकता है। मुझे इस त्रुटि पर यह त्रुटि मिल रही है। "var परिणाम = अभिव्यक्ति। लैम्ब्डा <एक्शन > (असाइनएक्सप्र, targetExpr, valueExpr);" – kans

+0

@ कंस - आह हाँ - आपको अभिव्यक्ति के पेड़ को थोड़ा अलग तरीके से बनाना होगा: 'fieldExpr' उत्पन्न करते समय 'बसबेस' से' T1' तक डाउनकास्ट। मेरा जवाब अपडेट किया है। –

+0

बहुत बढ़िया। बहुत बहुत धन्यवाद। यह बहुत अच्छा काम करता है। मैं अभिव्यक्तियों के आधार पर एक नियम इंजन बना रहा हूं। क्या आप इस पर अपने कुछ विचार फेंक सकते हैं .. – kans