2010-11-25 37 views
9

Tôi đang cố gắng tạo ra một thiết kế cho một số loại giao diện IExecutable. Tôi sẽ không đi vào chi tiết, nhưng vấn đề là tôi có một vài hành động cần được thực hiện từ một lớp cơ sở. Họ có thể có các thông số khác nhau (không có giao dịch lớn) và họ có thể/không thể trả về giá trị.Hành động đóng gói <T> và Func <T>?

Cho đến nay, đây là thiết kế của tôi:

public abstract class ActionBase 
{ 
    // ... snip ... 
} 

public abstract class ActionWithResultBase<T>: ActionBase 
{ 
    public abstract T Execute(); 
} 

public abstract class ActionWithoutResultBase: ActionBase 
{ 
    public abstract void Execute(); 
} 

Cho đến nay, mỗi người trong số hành động cụ thể của tôi cần phải là một đứa trẻ từ một trong hai ActionWithResultBase hoặc ActionWithoutResult cơ sở, nhưng tôi thực sự không thích điều đó. Nếu tôi có thể di chuyển định nghĩa Execute to ActionBase, xem xét rằng lớp bê tông có thể hoặc không thể trả về một giá trị, tôi sẽ đạt được mục tiêu của mình.

Có người nói với tôi điều này có thể được thực hiện bằng cách sử dụng Func và hành động, mà tôi hoàn toàn đồng ý, nhưng tôi không thể tìm cách để có điều đó vào một lớp duy nhất để người gọi sẽ biết nếu hành động để trả về một giá trị hay không.

Giới thiệu tóm tắt: Tôi muốn làm điều gì đó như:

// Action1.Execute() returns something. 
var a = new Action1(); 
var result = a.Execute(); 

// Action2.Execute() returns nothing. 
var b = new Action2(); 
b.Execute(); 
+0

Làm rõ: mục tiêu của tôi là để có một Execute trong ActionBase trừu tượng và tránh tạo ActionWithResultBase và ActionWithoutResultBase. – Alpha

Trả lời

6

Nếu bạn muốn có một giải pháp nhẹ, thì cách dễ nhất là viết hai lớp cụ thể. Một sẽ chứa một loại tài sản của Action và người kia là một loại tài sản của Func<T>:

public class ActionWithResult<T> : ActionBase { 
    public Func<T> Action { get; set; } 
} 

public class ActionWithoutResult : ActionBase { 
    public Action Action { get; set; } 
} 

Sau đó, bạn có thể xây dựng hai loại như thế này:

var a1 = new ActionWithResult<int> { 
    CanExecute = true, 
    Action =() => { 
    Console.WriteLine("hello!"); 
    return 10; 
    } 
} 

Nếu bạn không muốn thực hiện Action thuộc tính đọc/ghi, sau đó bạn có thể chuyển giao tác nhân làm đối số cho hàm tạo và làm cho thuộc tính chỉ đọc.

Thực tế là C# cần hai đại biểu khác nhau đại diện cho các chức năng và hành động khá khó chịu. Một giải pháp mà mọi người sử dụng là xác định loại Unit đại diện cho "không có giá trị trả lại" và sử dụng nó thay vì void. Sau đó, loại của bạn sẽ chỉ là Func<T> và bạn có thể sử dụng Func<Unit> thay vì Action. Loại Unit có thể trông như thế này:

public class Unit { 
    public static Unit Value { get { return null; } } 
} 

Để tạo một giá trị Func<Unit>, bạn sẽ viết:

Func<Unit> f =() => { /* ... */ return Unit.Value; } 
+0

Cảm ơn bạn. Tôi biết tôi đã chờ đợi rất lâu trước khi đánh dấu câu trả lời của bạn, nhưng tôi thực sự muốn xem có ai đó đưa ra giải pháp hay không. Ngay cả khi cách tiếp cận của bạn không hoàn toàn giải quyết vấn đề, nó thực sự là một câu trả lời tốt, và cho thấy một cách giải quyết tốt và tốt đẹp về cách khắc phục vấn đề này. – Alpha

0

Bạn biết rằng bạn có thể bỏ qua các giá trị trả về của một phương pháp đúng không? Bạn không để sử dụng.

+0

Có, bạn đúng về điều đó, nhưng tôi không nghĩ rằng đó sẽ là một cái gì đó thiết kế khôn ngoan để có.Ngoài ra, một người nào đó đề nghị tôi trở lại luôn luôn bool cho những người không có lợi nhuận mà chỉ cho thấy rằng các hoạt động đã thành công, nhưng tôi cũng nghĩ rằng đó là không chính xác. Nếu các hoạt động không nên trả lại một giá trị, tại sao lại buộc nó? – Alpha

+0

@Alpha - Tôi đồng ý với bạn rằng nếu một hoạt động không tự nhiên trả về thứ gì đó không nên. Trả về "true" không đúng vì bạn không nên trả về "false", bạn nên ném một ngoại lệ. Tuy nhiên tôi vẫn không hiểu mục tiêu thiết kế của bạn - mục tiêu của bạn dường như được dựa trên đường cú pháp và tôi không thấy những gì bạn đang hoàn thành mà bạn không thể chỉ nhận được Hành động và Func để giải quyết. Tôi nghĩ bạn cần giải thích vấn đề với họ là gì? – annakata

0

những gì về một cái gì đó đơn giản:

public class ActionExecuter 
{ 
    private MulticastDelegate del; 
    public ActionExecuter(MulticastDelegate del) 
    { 
     this.del = del; 
    } 

    public object Execute(params object[] p) 
    { 
     return del.DynamicInvoke(p); 
    } 
} 
+0

Cũng nghĩ về một cái gì đó tương tự, nhưng vấn đề của tôi là tôi sẽ không biết tại thời gian biên dịch mà loại được trả lại. Tôi luôn có thể làm cho ActionExecuter và thực hiện trả về T, nhưng nhịp đập đó là tôi không cần phải trả lại bất cứ thứ gì. – Alpha

1

Các giao diện sau đây nên làm mẹo - về bản chất, nó sao chép mẫu Nullable

public interface IActionBase 
{ 
     bool HasResult { get; } 
     void Execute() { } 
     object Result { get; } 
} 

public interface IActionBase<T> : IActionBase 
{ 
     new T Result { get; } 
} 

public sealed class ActionWithReturnValue<T> : IActionBase<T> 
{ 
     public ActionWithReturnValue(Func<T> action) { _action = action; } 
     private Func<T> _action; 

     public bool HasResult { get; private set; } 
     object IActionBase.Result { get { return this.Result; } } 
     public T Result { get; private set; } 
     public void Execute() 
     { 
      HasResult = false; 
      Result = default(T); 
      try 
      { 
       Result = _action(); 
       HasResult = true; 
      } 
      catch 
      { 
       HasResult = false; 
       Result = default(T); 
      } 
     } 

} 

public sealed class ActionWithoutReturnValue : IActionBase 
{ 
     public bool HasResult { get { return false; } } 
     object IActionBase.Result { get { return null; } } 
     public void Execute() { //... } 
} 
Các vấn đề liên quan