在我过去的几个项目中,我遇到了需要将多个参数传递给Threading.Timer
回调方法的情况。不幸的是,构造函数只接受一个object
参数。不想使用全局变量,我开始用来克服这个问题的模式是在创建计时器时传入一个匿名方法,并使用编译器捕获变量的能力,如下所示:
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();
}
}
所以我的问题是,这是一个公然的黑客攻击吗?是否有更好或推荐的方法来做到这一点?
答案 0 :(得分:7)
作为案例,您可以通过类封装所有参数:
public sealed class SendEmailParameters
{
public int RepeatCount { get; private set; }
...
}
private void SendRepeatedlyCallback(object state)
{
var parameters = (SendEmailParameters)state;
// ...
}
答案 1 :(得分:6)
这绝对没问题。从C#3开始,我个人使用lambda表达式 - 并使用单独的局部变量来避免在方法中间进行转换:
public void SendEmailsRepeatedly(IEnumerable<SimpleEmail> emails,
int sendRepeatedlyDelayMS)
{
Tokenizer tokenizer = new StandardTokenizer();
Action action = () => TokenizeAndSendEmails(emails, tokenizer);
sendRepeatedlyTimer = new Timer(SendRepeatedlyCallback, action, 0,
sendRepeatedlyDelayMS);
}
答案 2 :(得分:1)
他们是已知的参数吗?然后发送一个具有所需属性的专用对象。
答案 3 :(得分:1)
这很好。您还可以构建自己的类型,并使用它来包含要传递给回调的参数。只需将“对象状态”参数强制转换为您构造的类型,然后从类型中读取属性。
答案 4 :(得分:0)
您可以创建一个封装您需要传递的所有参数的类,也可以使用lambda表达式。这样的事情应该非常接近:
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)
{
...
}