在调用新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线程中执行代码。