在调用新Action时帮助理解C#语法

我是c#的新手,并不了解调用新操作的语法,甚至不了解操作是什么。 根据我对Port1_DataReceived的理解,我必须创建一个动作,因为我正处于新的阶段……任何人都可以详细说明为什么我需要这样做?

public Form1() { InitializeComponent(); SerialPort Port1 = new SerialPort("COM11", 57600, Parity.None, 8, StopBits.One); Port1.DataReceived += new SerialDataReceivedEventHandler(Port1_DataReceived); Port1.Open(); } private void Port1_DataReceived(object sender, SerialDataReceivedEventArgs e) { SerialPort Port = (SerialPort)sender; string Line = ""; int BytestoRead = Port.BytesToRead; Line = Port.ReadLine(); label1.Invoke(new Action(() => { label1.Text = Line; })); } 

我真正理解的代码片段是:

 label1.Invoke(new Action(() => { label1.Text = Line; })); 

有人可以分解这是做什么的……我相信这并不复杂,只是因为我以前从未见过这样的事情。 真正阻止我的语法是()=>新动作是指向下面的代码还是什么?

这使用称为“lambda表达式”的东西来创建一个与Action构造函数所期望的签名匹配的匿名委托。

你可以达到同样的效果:

 label1.Invoke(SetText); ... public void SetText() { label1.Text = Line; } 

或者像这样:

 label1.Invoke(new Action(SetText)); ... public void SetText() { label1.Text = Line; } 

或者像这样:

 label1.Invoke(new Action(delegate() { label1.Text = Line; })); 

或者像这样:

 label1.Invoke(delegate() { label1.Text = Line; }); 

或者像这样:

 label1.Invoke(() => label1.Text = Line); 

这些大多只是语法快捷方式,以便更容易表示操作。

请注意,lambda表达式通常具有参数。 当只有一个参数时,括号是可选的:

 list.ToDictionary(i => i.Key); 

当没有参数或多个参数时,括号是必要的,以使你明白你正在做什么。 因此, () =>

让我们一块一块地分解它。

 label1.Invoke( 

这是Control.Invoke方法。 以下是它的定义:

 public Object Invoke(Delegate method); 

在拥有控件的基础窗口句柄的线程上执行指定的委托。

这意味着你给它一个要调用的方法的引用, Control.Invoke将确保它在UI线程上被调用(这将在更新UI时防止交叉线程exception。)它需要一个默认的Delegate作为一个参数,这意味着你需要传递一个不带参数且没有返回值的方法。 这就是System.Action委托类型的来源:

 public delegate void Action(); 

使用lambda表达式,我们可以内联创建一个Action委托。 首先,我们指定委托类型:

 label1.Invoke(new Action( 

然后,我们将开始lambda语法。 一组空括号将表示lambda函数不带参数,之后的“箭头”表示我们要启动方法:

 label1.Invoke(new Action(() => 

现在,因为lambda方法没有返回值(但必须执行一个语句),我们需要在大括号中围绕我们想要在UI线程上执行的代码:

 label1.Invoke(new Action(() => { label1.Text = Line; } 

关闭剩余的括号,您将获得完整的语句。

 label1.Invoke(new Action(() => { label1.Text = Line; })); 

通常,当您想要向GUI添加内容并且您正在使用另一个线程时,您需要执行一些名为Invocation

要进行invocation您可以使用Controls Invoke方法或类似Application Dispatcher方法,这些方法通常采用Action 。 一个Action正是它听起来的样子,即要执行的事情。

在你的情况下,你正在做的是你想要一个文本行添加到GUI上的元素,所以你需要做的是创建一个Action (anonymouse方法),在这个动作中你只需要说“添加”这对我的控制“。 然后你Invoke它来避免交叉线程问题。

()=>只是一个创建方法的“快捷方式”(lambda方式),即匿名方法。 这意味着除了创建匿名方法的上下文之外,您无法从任何地方调用此方法。

您也可以Invoke “全局”方法,它不必是匿名方法。

Action是委托类型,换句话说,它封装了一个函数。 具体而言,Action封装了一个返回void的函数,而例如Func将封装一个带有返回值的函数。 这些很像C ++中的函数指针 – 本质上是对函数的引用,即封装行为的一种方式。

.Invoke()方法接受Action委托并运行它指向的函数。 在这种情况下,它指向的函数是lambda表达式:

 () => { label1.Text = Line } 

初始括号表示传递给函数的任何参数。 在这种情况下,没有参数,因此括号为空。 例如,如果你想传入两个字符串,你会这样做:

 var action = new Action( (x, y) => { // use x and y } 

无论如何,’=>’表达式本质上是函数的主体。 您可以访问本机构范围内括号中指定的变量。

总而言之,这是一种快速创建匿名函数的方法,基本上等同于以下内容:

 public void SetLine() { label1.Text = Line; } 

因此,您还可以通过执行以下操作来创建该Action对象:

 var action = new Action(SetLine) 

您在哪里传递要封装的方法的名称而不是传入lambda。 传入的内容被称为“方法组”。

这将生成一个匿名方法(精确地称为lambda )并将其传递给invoke方法。 Lambdas是一个很好的方法来获得你只需要一次的代码,所以你不需要很多辅助方法只做一件事。

这可以确保标签的文本在UI线程中运行。 Port1_DataReceived事件可能在后台线程中运行,并且不应从后台线程设置Label的文本值。 这可以防止这种情况发生。

我不知道label1是什么,但可以读作:

label1是一个Action,它接收另一个action作为参数。 它做了一些事情,当它调用在争论中收到的行动时。

现在,我已经读过这篇文章了,我可能会遇到一个问题 – label1不能成为一个Action。 因为它只是一个设置在这里的控件:label1.Text = Line;

您的应用中有错误;

编辑

对不起,请阅读:

http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx

在拥有控件的基础窗口句柄的线程上执行指定的委托。

代码是正确的。

行动是代表。 Label1.Invoke()用于执行代码label1.Text = line以避免交叉线程操作。 DataReceived事件的事件处理程序正在UI线程以外的其他线程上执行。 label1.Invoke()将在UI线程中执行代码。