2010-10-07 19 views
5

Vì vậy, tôi có điều này trong C# lib của tôi:từ khóa động cho phép "có thể" đơn nguyên?

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

Được sử dụng như:

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

tôi giữ các tập tin rác não của tôi đang cố gắng tìm ra cách để sử dụng 4 dynamic C# từ khóa để bật cú pháp này để thay thế :

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

tôi đã có một vài ví dụ mà tôi nghĩ làm việc nhưng họ bị phá vỡ khi bạn bắt đầu chaining các Maybe() s.

Bất kỳ ý tưởng nào?

(Tôi có đang lãng phí thời gian của mình không? Maybe() giành chiến thắng trên IfNotNull())?

+2

Có lẽ tôi đã có ý tưởng sai, nhưng sẽ không phải là ?? toán tử được sử dụng ở đây? – spender

+0

biến động không thể thấy các phương pháp tiện ích mà tôi tin. –

+0

Cá nhân tôi khá giống 'IfNotNull()' hiện tại của bạn. Vì bạn không thể sử dụng 'dynamic' với các phương thức mở rộng, cảm giác của tôi là mã có thể kết thúc là khủng khiếp. –

Trả lời

2

Tôi không nghĩ rằng việc sử dụng loại dynamic là một ý tưởng hay ở đây, vì cú pháp sẽ không trông đẹp hơn nhiều và bạn hy sinh loại an toàn (và IntelliSense) bằng cách gõ động.

Tuy nhiên, đây là ví dụ về những gì bạn có thể làm. Ý tưởng là bạn quấn các đối tượng vào DynamicWrapper (giá trị monadic của bạn :-)) có thể chứa giá trị null hoặc giá trị thực. Nó sẽ kế thừa từ DynamicObject và giao toàn bộ cuộc gọi đến các đối tượng thực tế (nếu có) hoặc ngay lập tức trở null (mà có thể ràng buộc 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); 
    } 
} 

Ví dụ chỉ hỗ trợ các thuộc tính và nó sử dụng phản ánh trong một khá cách không hiệu quả (tôi nghĩ rằng nó có thể được tối ưu hóa bằng cách sử dụng DLR). Dưới đây là một ví dụ như thế nào nó hoạt động:

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); 

Lưu ý rằng bạn có thể chuỗi các cuộc gọi tự do - chỉ có một cái gì đó đặc biệt ở phần đầu (Wrap) và ở phần cuối (Value), nhưng ở giữa, bạn có thể viết .Another.Another.Another... bao nhiêu lần bạn muốn.

1

Giải pháp này tương tự như Tomas 'ngoại trừ việc sử dụng CallSite để gọi thuộc tính trên đối tượng đích và cũng hỗ trợ truyền và gọi thêm vào Maybe (theo ví dụ của bạn).

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); 
    } 
} 

Các mã sau đây phải chứng minh nó được sử dụng:

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); 
Các vấn đề liên quan