简体   繁体   English

调用不带参数但使用局部参数的委托C#

[英]Invoke delegates without params but using local params c#

I find myself doing the following a lot, and i don't know if there is any side effects or not but consider the following in a WinForms C# app. 我发现自己做了很多事情,并且我不知道是否有任何副作用,但是在WinForms C#应用程序中考虑以下问题。 (please excuse any errors as i am typing the code in, not copy pasting anything) (请原谅我输入代码时出现的任何错误,而不是复制粘贴内容)

int a = 1;
int b = 2;
int c = 3;
this.Invoke((MethodInvoker)delegate()
{
    int lol = a + b + c;
});

Is there anything wrong with that? 那有什么问题吗? Or should i be doing the long way >_< 还是我应该做长途旅行> _ <

int a = 1;
int b = 2;
int c = 3;
TrippleIntDelegate ffs = new TrippleIntDelegate(delegate(int a_, int b_, int c_)
{
   int lol = a_ + b_ + c_;
});

this.Invoke(ffs);

The difference being the parameters are passed in instead of using the local variables, some pretty sweet .net magic. 区别在于传入参数而不是使用局部变量,这是一些非常不错的.net魔术。 I think i looked at reflector on it once and it created an entirely new class to hold those variables. 我想我曾经看过反射器,它创建了一个全新的类来容纳这些变量。

So does it matter? 那有关系吗? Can i be lazy? 我可以偷懒吗?

Edit: Note, do not care about the return value obviously. 编辑:注意,不要在意返回值。 Otherwise i'd have to use my own typed delegate, albeit i could still use the local variables without passing it in! 否则,我将不得不使用自己的类型化委托,尽管我仍然可以使用局部变量而无需将其传递!

The way you use it, it doesn't really make a difference. 您使用它的方式并没有真正的改变。 However, in the first case, your anonymous method is capturing the variables, which can have pretty big side effects if you don't know what your doing. 但是,在第一种情况下,您的匿名方法正在捕获变量,如果您不知道自己在做什么,可能会产生很大的副作用。 For instance : 例如 :

// 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

There's nothing wrong with passing in local variables as long as you understand that you're getting deferred execution. 只要知道延迟执行,传入局部变量就没有错。 If you write this: 如果您这样写:

int a = 1;
int b = 2;
int c = 3;
Action action = () => Console.WriteLine(a + b + c);
c = 10;
action();  // Or Invoke(action), etc.

The output of this will be 13, not 6. I suppose this would be the counterpart to what Thomas said; 这样的输出将是13,而不是6。我想这将与Thomas所说的相反。 if you read locals in a delegate, it will use whatever values the variables hold when the action is actually executed , not when it is declared . 如果您在委托中读取本地变量,它将使用变量在实际执行操作时 (而不是在声明时)持有的任何值。 This can produce some interesting results if the variables hold reference types and you invoke the delegate asynchronously. 如果变量具有引用类型,并且您异步调用委托,则这可能会产生一些有趣的结果。

Other than that, there are lots of good reasons to pass local variables into a delegate; 除此之外,还有很多充分的理由将局部变量传递给委托。 among other things, it can be used to simplify threading code. 除其他外,它可以用来简化线程代码。 It's perfectly fine to do as long as you don't get sloppy with it. 只要您不草率地做,那就很好。

Well, all of the other answers seem to ignore the multi-threading context and the issues that arise in that case. 好吧,所有其他答案似乎都忽略了多线程上下文以及在这种情况下出现的问题。 If you are indeed using this from WinForms, your first example could throw exceptions. 如果确实从WinForms使用此功能,则第一个示例可能会引发异常。 Depending on the actual data you are trying to reference from your delegate, the thread that code is actually invoked on may or may not have the right to access the data you close around. 根据您尝试从委托引用的实际数据,实际调用代码的线程可能有权访问或可能没有访问您所关闭的数据的权限。

On the other hand, your second example actually passes the data via parameters. 另一方面,您的第二个示例实际上是通过参数传递数据的。 That allows the Invoke method to properly marshal data across thread boundaries and avoid those nasty threading issues. 这样,Invoke方法就可以跨线程边界正确封送数据,并避免那些讨厌的线程问题。 If you are calling Invoke from, say, a background worker, then then you should use something like your second example (although I would opt to use the Action<T, ...> and Func<T, ...> delegates whenever possible rather than creating new ones). 如果要从后台工作人员调用Invoke,那么您应该使用类似第二个示例的方法(尽管无论何时我选择使用Action <T,...>和Func <T,...>委托而不是创建新的)。

From a style perspective I'd choose the paramater passing variant. 从样式角度来看,我将选择参数传递变量。 It's expresses the intent much easier to pass args instad of take ambients of any sort (and also makes it easier to test). 它表达了意图,使传递各种环境的args instad更加容易(并且也使测试更容易)。 I mean, you could do this: 我的意思是,您可以这样做:

public void Int32 Add()
{
    return this.Number1 + this.Number2
}

but it's neither testable or clear. 但它既不可测试也不清晰。 The sig taking params is much clearer to others what the method is doing... it's adding two numbers: not an arbatrary set of numbers or whatever. 带有参数的sig参数对其他人来说更清楚该方法在做什么……它添加了两个数字:不是任意的数字集合或其他任何东西。

I regularly do this with parms like collections which are used via ref anyway and don't need to be explicitlly 'returned': 我经常使用像pars这样的parm来执行此操作,无论如何通过ref都可以使用它们,而不必明确地“返回”:

public List<string> AddNames(List<String> names)
{
    names.Add("kevin");
    return names;
}

Even though the names collection is passed by ref and thus does not need to be explicitly returned, it is to me much clearer that the method takes the list and adds to it, then returns it back. 尽管名称集合是由ref传递的,因此不需要显式返回,但对我而言,更清楚的是该方法将列表添加到列表中,然后将其返回。 In this case, there is no technical reason to write the sig this way, but, to me, good reasons as far as clarity and therefore maintainablity are concerned. 在这种情况下, 没有技术上的理由以这种方式编写信号签名,但是对我来说,就清晰性和可维护性而言,有充分的理由。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM