2010-10-07 11 views
5

तो मैं अपने सी # lib में यह है:गतिशील कीवर्ड "शायद" monad सक्षम बनाता है?

public static TOut IfNotNull<TIn, TOut> 
    (this TIn instance, Func<TIn, TOut> func) 
{ 
    return instance == null ? default(TOut) : func(instance); 
} 

प्रयुक्त की तरह:

DateTime? expiration = promo.IfNotNull(p => p.TermsAndConditions.Expiration) 
          .IfNotNull(e => e.Date); 

मैं कैसे इस वाक्य रचना के बजाय सक्षम करने के लिए सी # 4 dynamic कीवर्ड का उपयोग करने के पता लगाने की कोशिश मेरी दिमाग wracking रखने :

DateTime? expiration = promoOffer.TermsAndConditions.Maybe() 
           .Expiration.Maybe() 
           .Date; 

मैं कुछ उदाहरण है कि मैं काम नहीं सोचा था, लेकिन वे खराब हो गई है जब आप चेनिंग Maybe() रों शुरू करते हैं।

कोई विचार?

(क्या मैं अपना समय बर्बाद कर रहा हूं? Maybe()IfNotNull() से अधिक जीत है)?

+2

शायद मैं गलत विचार मिल गया है, लेकिन नहीं होगा ?? ऑपरेटर का उपयोग यहां किया जा सकता है? – spender

+0

गतिशील चर मुझे विश्वास विधियों को नहीं देख सकते हैं। –

+0

व्यक्तिगत रूप से मुझे वर्तमान में 'IfNotNull()' पसंद है। चूंकि आप विस्तार विधियों के साथ 'गतिशील' का उपयोग नहीं कर सकते हैं, मेरी भावना यह है कि कोड भयानक हो सकता है। –

उत्तर

2

मुझे नहीं लगता कि dynamic प्रकार का उपयोग करना एक अच्छा विचार है, क्योंकि वाक्यविन्यास बहुत बेहतर दिखने वाला नहीं है और आप गतिशील टाइपिंग का उपयोग करके प्रकार की सुरक्षा (और IntelliSense) को त्याग देते हैं।

हालांकि, यहां एक उदाहरण दिया गया है कि आप क्या कर सकते हैं। विचार यह है कि आप वस्तुओं को DynamicWrapper (अपने monadic value :-)) में लपेटते हैं जिसमें या तो null मान या वास्तविक मान हो सकता है। यह DynamicObject से विरासत हैं और वास्तविक वस्तु के लिए सभी कॉल प्रतिनिधि (अगर कोई है कोई है) या तुरंत null वापसी (कि monadic बाँध होगा):

class DynamicWrapper : DynamicObject { 
    public object Object { get; private set; } 
    public DynamicWrapper(object o) { Object = o; } 
    public override bool TryGetMember(GetMemberBinder binder, out object result) { 
    // Special case to be used at the end to get the actual value 
    if (binder.Name == "Value") result = Object; 
    // Binding on 'null' value - return 'null' 
    else if (Object == null) result = new DynamicWrapper(null); 
    else { 
     // Binding on some value - delegate to the underlying object 
     var getMeth = Object.GetType().GetProperty(binder.Name).GetGetMethod(); 
     result = new DynamicWrapper(getMeth.Invoke(Object, new object[0])); 
    return true; 
    } 
    public static dynamic Wrap(object o) { 
    return new DynamicWrapper(o); 
    } 
} 

उदाहरण केवल गुण का समर्थन करता है और यह एक बहुत में प्रतिबिंब का उपयोग करता है अक्षम तरीका (मुझे लगता है कि इसे डीएलआर का उपयोग करके अनुकूलित किया जा सकता है)। यहाँ एक उदाहरण यह कैसे काम करता है:

class Product { 
    public Product Another { get; set; } 
    public string Name { get; set; } 
} 

var p1 = new Product { Another = null }; 
var p2 = new Product { Another = new Product { Name = "Foo" } }; 
var p3 = (Product)null; 

// prints '' for p1 and p3 (null value) and 'Foo' for p2 (actual value) 
string name = DynamicWrapper.Wrap(p1).Another.Name.Value; 
Console.WriteLine(name); 

ध्यान दें कि आप कॉल स्वतंत्र रूप से श्रृंखला कर सकते हैं - वहाँ केवल शुरुआत (Wrap) में कुछ खास है और अंत (Value) पर है, लेकिन बीच में, आप कर सकते हैं जितनी बार चाहें .Another.Another.Another... लिखें।

1

यह समाधान टॉमस के समान है, सिवाय इसके कि यह लक्ष्य उदाहरण पर गुणों का आह्वान करने के लिए CallSite का उपयोग करता है और Maybe (आपके उदाहरण के अनुसार) कास्टिंग और अतिरिक्त कॉल का भी समर्थन करता है।

public static dynamic Maybe(this object target) 
{ 
    return new MaybeObject(target); 
} 

private class MaybeObject : DynamicObject 
{ 
    private readonly object _target; 

    public MaybeObject(object target) 
    { 
     _target = target; 
    } 

    public override bool TryGetMember(GetMemberBinder binder, 
             out object result) 
    { 
     result = _target != null ? Execute<object>(binder).Maybe() : this; 
     return true; 
    } 

    public override bool TryInvokeMember(InvokeMemberBinder binder, 
             object[] args, out object result) 
    { 
     if (binder.Name == "Maybe" && 
      binder.ReturnType == typeof (object) && 
      binder.CallInfo.ArgumentCount == 0) 
     { 
      // skip extra calls to Maybe 
      result = this; 
      return true; 
     } 

     return base.TryInvokeMember(binder, args, out result); 
    } 

    public override bool TryConvert(ConvertBinder binder, out object result) 
    { 
     if (_target != null) 
     { 
      // call Execute with an appropriate return type 
      result = GetType() 
       .GetMethod("Execute", BindingFlags.NonPublic | BindingFlags.Instance) 
       .MakeGenericMethod(binder.ReturnType) 
       .Invoke(this, new object[] {binder}); 
     } 
     else 
     { 
      result = null; 
     } 
     return true; 
    } 

    private object Execute<T>(CallSiteBinder binder) 
    { 
     var site = CallSite<Func<CallSite, object, T>>.Create(binder); 
     return site.Target(site, _target); 
    } 
} 

निम्नलिखित कोड प्रयोग में यह प्रदर्शित करना चाहिए:

var promoOffer = new PromoOffer(); 
var expDate = promoOffer.TermsAndConditions.Maybe().Expiration.Maybe().Date; 
Debug.Assert((DateTime?) expDate == null); 

promoOffer.TermsAndConditions = new TermsAndConditions(); 
expDate = promoOffer.TermsAndConditions.Maybe().Expiration.Maybe().Date; 
Debug.Assert((DateTime?) expDate == null); 

promoOffer.TermsAndConditions.Expiration = new Expiration(); 
expDate = promoOffer.TermsAndConditions.Maybe().Expiration.Maybe().Date; 
Debug.Assert((DateTime?) expDate == null); 

promoOffer.TermsAndConditions.Expiration.Date = DateTime.Now; 
expDate = promoOffer.TermsAndConditions.Maybe().Expiration.Maybe().Date; 
Debug.Assert((DateTime?) expDate != null); 
संबंधित मुद्दे