2011-11-23 32 views
7

Trong một vài dự án trước đây của tôi, tôi đã gặp phải các tình huống mà tôi cần phải chuyển nhiều tham số đến phương thức gọi lại Threading.Timer. Thật không may, hàm tạo chỉ chấp nhận một tham số object duy nhất. Không muốn sử dụng các biến toàn cục, mẫu mà tôi đã bắt đầu sử dụng để khắc phục vấn đề này là chuyển qua một phương thức ẩn danh khi bộ đếm thời gian được tạo và sử dụng khả năng của trình biên dịch để nắm bắt các biến số của tôi, như vậy:Cách tốt nhất để chuyển nhiều tham số đến phương thức gọi lại Threading.Timer là gì?

public void SendEmailsRepeatedly(IEnumerable<SimpleEmail> emails, int sendRepeatedlyDelayMS) 
{ 
    Tokenizer tokenizer = new StandardTokenizer(); 

    sendRepeatedlyTimer = new Timer(
     SendRepeatedlyCallback, 
     (Action)delegate() 
     { 
      TokenizeAndSendEmails(emails, tokenizer); 
     }, 
     0, 
     sendRepeatedlyDelayMS); 
} 

private void SendRepeatedlyCallback(object state) 
{ 
    if (!abort) 
    { 
     Action sendEmails = (Action)state; 
     sendEmails(); 
    } 
} 

Vì vậy, câu hỏi của tôi là, đây có phải là một bản hack lỗi không? Có cách nào tốt hơn hoặc được khuyến nghị để làm điều này?

Trả lời

7

Là một trường hợp bạn có thể gói gọn tất cả các thông số của một lớp :

public sealed class SendEmailParameters 
{ 
    public int RepeatCount { get; private set; } 
    ... 
} 

private void SendRepeatedlyCallback(object state) 
{ 
    var parameters = (SendEmailParameters)state; 

    // ... 
} 
+2

Tôi sẽ đưa nó cho bạn vì đây có lẽ là cách tiếp cận "dễ đọc nhất" và Tôi chắc chắn rằng đồng nghiệp của tôi đã nhầm lẫn đủ với hầu hết mã của tôi. –

+1

Tôi không thấy bất kỳ lợi ích nào trong việc này. Tại sao tạo ra một lớp học cho mình khi trình biên dịch có thể làm tất cả công việc soạn sẵn cho bạn? –

+3

@Jon Bạn có một điểm và tôi cũng là một fan hâm mộ đẩy bàn đạp vào kim loại với trình biên dịch. Tuy nhiên, tôi cũng nghĩ rằng có một số tình huống tốt để làm cho mã tiết kiệm hơn một chút chỉ để những người khác có thể hiểu được nó mà không gặp khó khăn gì. Ngược lại, có thể lập luận rằng tôi chỉ nên thêm nhiều ý kiến ​​nội tuyến hơn. –

6

Điều đó hoàn toàn ổn. Tính đến C# 3 Tôi muốn sử dụng một biểu thức lambda thay vào đó, cá nhân - và sử dụng một biến địa phương riêng biệt để tránh các diễn viên ở giữa một phương pháp:

public void SendEmailsRepeatedly(IEnumerable<SimpleEmail> emails, 
           int sendRepeatedlyDelayMS) 
{ 
    Tokenizer tokenizer = new StandardTokenizer(); 
    Action action =() => TokenizeAndSendEmails(emails, tokenizer);  
    sendRepeatedlyTimer = new Timer(SendRepeatedlyCallback, action, 0, 
            sendRepeatedlyDelayMS); 
} 
+0

Rất ấn tượng. Tôi là một chút bối rối là tại sao nó xấu để đúc ở giữa phương pháp? Đây có phải là chỉ vì lợi ích của khả năng đọc hoặc nó sẽ gây ra vấn đề thực tế? –

+3

@RepoMan: Đó là "chỉ" vì mục đích dễ đọc - nhưng đó là một "chỉ" lớn từ quan điểm của tôi :) –

1

Chúng có phải là thông số đã biết không? Sau đó gửi một mục đích được xây dựng đối tượng với tất cả các thuộc tính mà bạn cần.

1

Điều này là tốt. Bạn cũng có thể xây dựng kiểu của riêng mình và sử dụng loại đó để chứa các tham số mà bạn muốn chuyển vào cuộc gọi lại của mình. Chỉ cần đưa tham số 'trạng thái đối tượng' vào loại mà bạn đã xây dựng và đọc các thuộc tính từ loại của bạn.

0

Bạn có thể tạo một lớp đóng gói tất cả các đối số bạn cần chuyển hoặc bạn có thể sử dụng biểu thức lambda. Một cái gì đó như thế này nên được khá gần:

public void SendEmailsRepeatedly(IEnumerable<string> emails, int sendRepeatedlyDelayMS) 
{ 
    AutoResetEvent resetEvent = new AutoResetEvent(false);  
    Tokenizer tokenizer = new StandardTokenizer();   

    var timer = new Timer(x => SendRepeatedlyCallback(x, emails, tokenizer), resetEvent, 0, sendRepeatedlyDelayMS); 
} 

static void SendRepeatedlyCallback(object state, IEnumerable<string> emails, StandardTokenizer tokenizer) 
{ 
    ... 
} 
Các vấn đề liên quan