C# 调用不带参数但使用本地参数c的委托# 我发现自己做了很多事情,我不知道是否有副作用,但是在WiFrm C应用程序中考虑下面的内容。 (请原谅我在输入代码时出现的任何错误,不要复制粘贴任何内容)

C# 调用不带参数但使用本地参数c的委托# 我发现自己做了很多事情,我不知道是否有副作用,但是在WiFrm C应用程序中考虑下面的内容。 (请原谅我在输入代码时出现的任何错误,不要复制粘贴任何内容),c#,parameters,delegates,lazy-evaluation,C#,Parameters,Delegates,Lazy Evaluation,这有什么问题吗?或者我应该按照你使用它的方式来做这件事吗?这并没有什么区别。然而,在第一种情况下,您的匿名方法是捕获变量,如果您不知道自己在做什么,这可能会产生很大的副作用。例如: // No capture : int a = 1; Action<int> action = delegate(int a) { a = 42; // method parameter a }); action(a); Console.WriteLine(a); // 1 // Capture

这有什么问题吗?或者我应该按照你使用它的方式来做这件事吗?这并没有什么区别。然而,在第一种情况下,您的匿名方法是捕获变量,如果您不知道自己在做什么,这可能会产生很大的副作用。例如:

// No capture :
int a = 1;
Action<int> action = delegate(int a)
{
    a = 42; // method parameter a
});
action(a);
Console.WriteLine(a); // 1

// Capture of local variable a :
int a = 1;
Action action = delegate()
{
    a = 42; // captured local variable a
};
action();
Console.WriteLine(a); // 42
//无捕获:
INTA=1;
Action=委托(int a)
{
a=42;//方法参数a
});
行动(a);
控制台。写入线(a);//1.
//捕获局部变量a:
INTA=1;
Action=delegate()
{
a=42;//捕获的局部变量a
};
动作();
控制台。写入线(a);//42

从样式的角度来看,我会选择paramater传递变量。它表达了更容易通过args instad或任何类型的take环境的意图(也更容易测试)。我是说,你可以这样做:

public void Int32 Add()
{
    return this.Number1 + this.Number2
}
但它既不可测试也不清晰。sig-taking-params对其他人来说更清楚该方法在做什么。。。它增加了两个数字:不是任意的一组数字或其他什么

我经常使用parms这样的集合,这些集合无论如何都是通过ref使用的,不需要明确地“返回”:

public List<string> AddNames(List<String> names)
{
    names.Add("kevin");
    return names;
}
公共列表地址名(列表名)
{
姓名。添加(“凯文”);
返回姓名;
}

即使名称集合是通过ref传递的,因此不需要显式返回,但对我来说,更清楚的是,该方法获取列表并添加到其中,然后返回。在这种情况下,以这种方式编写sig没有技术上的原因,但是,对我来说,就清晰性和可维护性而言,有很好的理由。

传递局部变量没有什么错,只要您知道您得到的是延迟执行。如果你这样写:

int a = 1;
int b = 2;
int c = 3;
Action action = () => Console.WriteLine(a + b + c);
c = 10;
action();  // Or Invoke(action), etc.
这个的输出将是13,而不是6。我想这就是托马斯所说的对应物;如果在委托中读取局部变量,则在实际执行操作时,而不是在声明操作时,它将使用变量持有的任何值。如果变量包含引用类型,并且您异步调用委托,则会产生一些有趣的结果


除此之外,将局部变量传递给委托还有很多很好的理由;除此之外,它还可以用来简化线程代码。只要你不太草率,这样做是完全可以的。

好吧,所有其他答案似乎都忽略了多线程上下文以及在这种情况下出现的问题。如果您确实是从WinForms中使用它,那么您的第一个示例可能会抛出异常。根据您试图从委托引用的实际数据,实际调用代码的线程可能有权或无权访问您关闭的数据


另一方面,第二个示例实际上通过参数传递数据。这允许Invoke方法跨线程边界正确封送数据,并避免那些讨厌的线程问题。如果您是从后台工作者调用Invoke,那么您应该使用类似于第二个示例的内容(尽管我会尽可能选择使用Action和Func委托,而不是创建新的委托)。

我说的更多的是委托,而不是方法,这是一个不同的故事。我知道。我的观点是为了清晰而不是简洁。对于委托和方法都是如此。实际上,将局部变量传递给委托可以消除某些多线程问题,只要所有内容都在范围内。一个这样的例子是使用
ThreadPool
并行化任务,在完成时向每个任务传递一个
ManualResetEvent
,然后等待所有等待句柄。比备选方案简单得多(无论如何,在.NET3.5中)。这样的线程是没有问题的,尽管如果你旋转一个线程,然后在线程完成之前让局部变量超出范围(正如我前面提到的),这显然会成为一个问题。我认为,接近局部变量是一个很滑的斜坡。我可能会想到相反的情况,但是当涉及到WinForms和控件时,控件及其子级和属性只能由UI线程访问。试图从另一个线程访问它们的任何部分都会导致异常。我相信WPF和其他基于XAML的东西也有类似的问题。如果你删除了用户界面,那么我不认为有任何问题……但是当你有了用户界面,这是需要注意的。是的,这正是我想要的
public List<string> AddNames(List<String> names)
{
    names.Add("kevin");
    return names;
}
int a = 1;
int b = 2;
int c = 3;
Action action = () => Console.WriteLine(a + b + c);
c = 10;
action();  // Or Invoke(action), etc.