2013-04-02 70 views
7

Tôi có nhiều phương pháp yêu cầu một số ghi nhật ký có cùng mẫu. Một số phương pháp cần trả lại một số giá trị, một số thì không. Tôi đã tạo một phương thức với tham số Action để tránh sao chép tất cả logic. Nó trông giống như thế này:Kết hợp hành động và Func trong một tham số

private void Execute(Action action) 
{ 
    Logger.Start(); 
    try 
    { 
     action(); 
    } 
    catch(Exception exception) 
    { 
     Logger.WriteException(); 
     throw; 
    } 
    finally 
    { 
     Logger.Finish(); 
    } 
} 

Bây giờ tôi có một số cuộc gọi mà thích rằng

public void DoSomething(string parameter) 
{ 
    Execute(() => GetProvider(parameter).DoSomething()); 
} 

Nhưng tôi cần một số chức năng mà trở về giá trị. Cách tốt nhất để làm điều đó là gì? Tôi đã tìm thấy hai bây giờ:

1) Tạo một bản sao của phương pháp Execute với Func

private T Execute<T>(Func<T> action) 
{ 
    Logger.Start(); 
    try 
    { 
     return action(); 
    } 
    catch(Exception exception) 
    { 
     Logger.WriteException(); 
     throw; 
    } 
    finally 
    { 
     Logger.Finish(); 
    } 
} 

phương pháp này hoạt động nhưng có một số bản sao dán là tốt.

2) Lừa tham số vào là một hành động:

public Result DoSomething(string parameter) 
{ 
    Result result = null; 
    Execute(() => result = GetProvider(parameter).DoSomething()); 
    return result; 
} 

này không yêu cầu dán sao chép nhưng không trông rất đẹp.

Có cách nào để tham gia Hành động và Func bằng cách nào đó để tránh bất kỳ phương pháp nào trong số này hoặc có thể có cách khác để đạt được kết quả tương tự không?

+0

Tôi sử dụng phương pháp tiếp cận thứ hai của bạn. Tôi không thể tìm thấy một cách hay để làm điều đó theo bất kỳ cách nào khác, vì vậy tôi sẽ quan tâm để xem bất kỳ câu trả lời nào cho câu hỏi của bạn! –

+0

Có vẻ như liên quan: http://stackoverflow.com/q/4279210/55209 –

+0

xem xét sử dụng các khía cạnh http://stackoverflow.com/questions/1416880/aspect-oriented-programming-in-c-sharp – Lanorkin

Trả lời

6

Một lựa chọn thứ ba là vẫn quá tải Execute, nhưng làm công việc phiên bản Action về phiên bản Func:

private void Execute(Action action) 
{ 
    // We just ignore the return value here 
    Execute(() => { 
     action(); 
     return 0; 
    }); 
} 

Dĩ nhiên tất cả điều này sẽ đơn giản hơn nếu void là giống như một loại "thực" (như Unit trong F # et al), tại thời điểm đó chúng tôi chỉ có thể có Task<T> thay vì TaskTask<T> cũng ...

+0

Tôi nghĩ về phương pháp này là tốt, nhưng thậm chí không bao gồm nó vì nó trông khá bẩn với tôi khi bạn giả vờ là một Func bằng cách sử dụng int cho một loại chung và trả về giá trị mặc định mà không bao giờ được sử dụng. –

+1

@IlyaChernomordik: Tôi nghĩ rằng đó là sạch hơn việc gán cho một biến cục bộ trong một 'Hành động' để làm cho nó hoạt động giống như một' Func ', cá nhân. Nhưng vấn đề là nó nằm trong 'Execute', không phải trong' DoSomething' - bạn chỉ cần "bẩn" này ở một nơi duy nhất, không phải trong nhiều người gọi. –

+0

@ JonSkeet Có thể bạn là đúng và nó ít bẩn. Hoặc có thể là cùng một bẩn nhưng ở một nơi :) –

3

Tạo một bản sao của Execute có thể chuyển đổi các Func int o an Action. Bạn chỉ phải viết mã xấu đó một lần và bạn không kết thúc với bản sao thứ hai hoàn chỉnh của phương thức Execute:

private T Execute<T>(Func<T> func) 
{ 
    T result = default(T); 
    this.Execute(() => { result = func(); }); 
    return result; 
} 

... 

public Result DoSomething(string parameter) 
{ 
    return Execute(() => GetProvider(parameter).DoSomething()); 
} 
+0

@ p.s.w.g Vấn đề với cách tiếp cận này là tôi có cả hai loại giá trị và các lớp như trả về. Và mã của bạn sẽ không biên dịch vì biến cục bộ không được khởi tạo trước khi trả về. –

+0

@IlyaChernomordik Chỉ cần khởi tạo 'kết quả' thành' mặc định (T) '. Xem câu trả lời cập nhật của tôi. –

+0

@ p.s.w.g Phải, không nghĩ về mặc định. Trên thực tế bản cập nhật này là khá hợp lý cho phương pháp thứ hai của tôi, không biết tại sao tôi đã không mặc dù về nó. –

2

Đây là một tùy chọn khác. Thay vì có khung ghi nhật ký, hãy gọi mã thực tế của bạn, để mã thực sự của bạn gọi khung khai thác gỗ. Một cái gì đó như thế này sẽ làm các trick (rất đơn giản).

public class LoggerScope : IDisposable { 

    private bool disposed; 

    public LoggerScope() { 
     Logger.Start(); 
    } 

    public void Dispose() { 
     if(!disposed) { 
      Logger.Finish(); 
      disposed = true; 
     } 
    } 
} 

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

 using(var scope = new LoggerScope()) { 
      // actual code goes here 
     } 

Xử lý ngoại lệ riêng bằng cách bắt và đăng nhập họ chỉ một lần ở cấp cao nhất của mã của bạn.

Ưu điểm:

  • Tránh sự cần thiết của lambdas khắp nơi, vì vậy ngoại trừ vết đống là một chút bụi.
  • Bạn có thể thêm dữ liệu theo ngữ cảnh tùy ý vào lớp LoggerScope, ví dụ: GUID, dấu thời gian, văn bản mô tả tác vụ lôgíc.
+0

Điểm bất lợi là bạn phải tạo ra một lớp đặc biệt cho từng trường hợp đặc biệt. Mặc dù tôi đã sử dụng phương pháp này để thực hiện khóa trình đọc của người viết. –