简体   繁体   English

如何使用委托?

[英]How to use delegate?

I use delegate in different threads.我在不同的线程中使用委托。

This is work:这是工作:

delegate void AppendTextDelegate(string text);

 public void appendTextData(string text)
 {
        if (richBox1.InvokeRequired)
        {
            richBox1.Invoke(new AppendTextDelegate(appendTextData), new object[] { text           });
        }
        else
        {
            richBox1.AppendText(text);
        }
    }

This is work too:这也是工作:

delegate void ChangeValDelegate(int value);

progressBar1.Invoke(new ChangeValDelegate((s) => progressBar1.Value = s), 0);

This is not work:这是行不通的:

delegate void ClearDelegate();

listView.Invoke(new ClearDelegate()); <-- "Does not contain a constructor that takes 0 arguments"

I think you are creating lots of delegates to work with your controls.我认为您正在创建大量代表来使用您的控件。 Also, you are using each control (listview, richtextbox...) when you can use only one of them, for example, your form.此外,当您只能使用其中一个控件(例如表单)时,您正在使用每个控件(listview、richtextbox ...)。

The problem here is that you must work with your controls in the same thread in which they was created.这里的问题是您必须在创建控件的同一线程中使用控件。 You use InvokeRequired to check this and run the code in other thread (main thread) when you are running code in a different thread.当您在不同的线程中运行代码时,您使用 InvokeRequired 来检查并在其他线程(主线程)中运行代码。

Usually, all your controls and forms are created in the main thread, so you can check only with one of your controls to know when it's safe to access to the controls.通常,您的所有控件和 forms 都是在主线程中创建的,因此您可以仅使用其中一个控件进行检查,以了解何时可以安全地访问这些控件。

Here you have a way to do the same in a more compact and simple way.在这里,您可以以更紧凑和简单的方式执行相同的操作。 First, add this field to your form:首先,将此字段添加到您的表单中:

private static SynchronizationContext Context;

And set it's value in your form contructor:并在您的表单构造函数中设置它的值:

Context = SynchronizationContext.Current;

SynchronizationContext.Current it's different in differents contexts but in the form contructor you know that the context it's the Form context (your main thread). SynchronizationContext.Current它在不同的上下文中有所不同,但在表单构造器中,您知道上下文是表单上下文(您的主线程)。 So, we save in Context the context in which we can access to the form (and all it's controls).因此,我们在Context中保存我们可以访问表单(及其所有控件)的上下文。

Add this method to simplify the work with controls:添加此方法以简化控件的工作:

private static void RunInMainThread(Action operation)
{
    if (Context != SynchronizationContext.Current)
    {
        Context.Post(o =>
        {
            try
            {
                operation();
            }
            catch (ObjectDisposedException)
            {
            }
        }, null);
    }
    else
    {
        operation();
    }
}

It's similar to InvokeRequired but with SynchronizationContext .它类似于InvokeRequired但具有SynchronizationContext If you are in the right place (same context of your form), simply run the action.如果您在正确的位置(与表单的上下文相同),只需运行该操作。 In other case, run the action in the form context.在其他情况下,在表单上下文中运行操作。 Here you can see that it's done using Post .在这里您可以看到它是使用Post完成的。 In this way, you avoid deadlocks and accept that the code may run with a bit delay (because is enqueued).通过这种方式,您可以避免死锁并接受代码可能会延迟运行(因为已入队)。

And why an Action ?为什么是Action because you can run anything using the action, leaving the parameters outside of the method invocation.因为您可以使用该操作运行任何东西,而将参数留在方法调用之外。 You don't need lots of methods with different parameters to control any access to your controls.您不需要许多具有不同参数的方法来控制对控件的任何访问。

For example:例如:

public void appendTextData(string text)
{
    RunInMainThread(() =>
    {
        richBox1.AppendText(text);
    });

    // Beeing a single line, you can compact the code
    //RunInMainThread(() => richBox1.AppendText(text));
}

You run your AppendText with the text parameter, but you don't need this parameter in your RunInMainThread method.您使用text参数运行AppendText ,但在RunInMainThread方法中不需要此参数。

Another example, to set progress bar to start and clear the listview:另一个例子,设置进度条来启动和清除列表视图:

RunInMainThread(() =>
{
    progressBar1.Value = 0;
    listview.Clear();
});

And about the delegates: they are like a contract, like an interface.关于代表:它们就像一个合同,一个接口。 A delegate hasn't code, only tell you about the arguments... but without code.代表没有代码,只告诉您有关 arguments 的信息......但没有代码。

In your code:在您的代码中:

progressBar1.Invoke(
    new ChangeValDelegate(
        (s) => progressBar1.Value = s), 0);

You are running an implementation that match with your delegate (return void and get an integer as a parameter).您正在运行与您的委托匹配的实现(返回 void 并获取 integer 作为参数)。

Is like:就好像:

var d = new ChangeValDelegate(OnChangeValDelegate);
BeginInvoke(d, new object[] { 0 });

Having:有:

public void OnChangeValDelegate(int value)
{
    progressBar1.Value = value;
}

As you see, you need an implementation.如您所见,您需要一个实现。 Here:这里:

listView.Invoke(
    new ClearDelegate(/* NO IMPLEMENTATION */));

You need the code (matching your delegate) that clear the listview.您需要清除列表视图的代码(匹配您的委托)。

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

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