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

在调用新操作时帮助理解C#语法,c#,action,invoke,C#,Action,Invoke,我是c#新手,不理解调用新动作的语法,甚至不理解动作是什么。根据我在Port1_DataReceived中的理解,我必须创建一个操作,因为我处于一个新的阶段。。。有人能详细说明我为什么需要这样做吗 public Form1() { InitializeComponent(); SerialPort Port1 = new SerialPort("COM11", 57600, Parity.None, 8, StopBits.One); Port1.DataReceived

我是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;
          }));

有人能解释一下这是怎么回事吗。。我相信这没什么复杂的,只是我以前从未见过这样的事情。真正阻碍我的语法是
()=>
新操作指向下面的代码或其他东西

这是生成一个匿名方法(确切地说是a)并将其传递给invoke方法。lambda是一种让代码只需要一次的好方法,因此不需要很多助手方法只做一件事。

这使用了一种称为“lambda表达式”的方法来创建一个匿名委托,该委托与动作构造函数所期望的签名相匹配

您可以实现如下相同的效果:

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);
或者像这样:

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);
或者像这样:

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);
或者像这样:

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);
或者像这样:

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);

当没有参数或有多个参数时,括号是必要的,以便清楚地表明您在做什么。因此,
()=>

这确保标签的文本在UI线程中运行。Port1_DataReceived事件可能会在后台线程中运行,标签的文本值不应在后台线程中设置。这就避免了这种情况的发生。

我不知道标签1是什么,但可以理解为:

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

现在,我已经读到了,我可能是一个问题——label1不能是一个动作。因为它只是一个在这里设置的控件:label1.Text=Line

你的应用程序中有一个错误

编辑

对不起,请阅读:

在拥有控件底层窗口句柄的线程上执行指定的委托


代码是正确的。

通常,当您想向您的GUI添加一些内容,并且您正在从另一个线程工作时,您需要执行名为调用的操作

要进行
调用
您可以使用控件
调用
方法或类似于
应用程序调度程序的方法,这些方法通常采取
操作
。一个
动作
就是它听起来的样子,需要执行的东西

在您的情况下,您要做的是向GUI上的元素添加一行文本,因此您需要做的是创建一个
操作
(匿名方法),在这个操作中,您只需说“将此添加到我的控件”。然后调用
,以避免交叉线程问题

()=>
只是创建匿名方法的“快捷方式”(lambda方式)。这意味着您只能从创建匿名方法的上下文中调用它


您还可以调用“全局”方法,它不必是匿名方法。

操作是委托类型,换句话说,它封装了一个函数。具体地说,一个操作封装了一个返回void的函数,而例如一个Func将用一个返回值封装一个函数。这些都类似于C++中的函数指针——本质上是对函数的引用,即封装行为的方法。p> .Invoke()方法接受动作委托并运行它所指向的函数。在这种情况下,它指向的函数是lambda表达式:

() => { label1.Text = Line }
初始括号表示传递到函数中的任何参数。在这种情况下,没有参数,因此括号为空。例如,如果您想传入两个字符串,您可以执行以下操作:

var action = new Action<string, string>( (x, y) => { // use x and y }
因此,您还可以通过执行以下操作来创建该动作对象:

var action = new Action(SetLine) 

其中,您要传递要封装的方法的名称,而不是传递lambda。传入的内容称为“方法组”

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

让我们逐一分解它

label1.Invoke(
这就是方法。以下是它的定义:

public Object Invoke(Delegate method);
在拥有控件底层窗口句柄的线程上执行指定的委托

这意味着您给它一个要调用的方法的引用,并且
控件。Invoke
将确保它在UI线程上被调用(这将防止在更新UI时出现跨线程异常)。它将默认的
委托
作为参数,这意味着您需要向它传递一个不带参数且没有返回值的方法。这就是委托类型的作用:

public delegate void Action();
使用lambda表达式,我们可以创建
操作
内联委托。首先,我们指定委托类型:

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;
}));
因此,在执行“label1.Invoke(()=>label1.Text=Line)”或甚至“label1.Invoke(newaction(()=>label1.Text=Lin)”时