簡體   English   中英

如何使用委托?

[英]How to use delegate?

我在不同的線程中使用委托。

這是工作:

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

這也是工作:

delegate void ChangeValDelegate(int value);

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

這是行不通的:

delegate void ClearDelegate();

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

我認為您正在創建大量代表來使用您的控件。 此外,當您只能使用其中一個控件(例如表單)時,您正在使用每個控件(listview、richtextbox ...)。

這里的問題是您必須在創建控件的同一線程中使用控件。 當您在不同的線程中運行代碼時,您使用 InvokeRequired 來檢查並在其他線程(主線程)中運行代碼。

通常,您的所有控件和 forms 都是在主線程中創建的,因此您可以僅使用其中一個控件進行檢查,以了解何時可以安全地訪問這些控件。

在這里,您可以以更緊湊和簡單的方式執行相同的操作。 首先,將此字段添加到您的表單中:

private static SynchronizationContext Context;

並在您的表單構造函數中設置它的值:

Context = SynchronizationContext.Current;

SynchronizationContext.Current它在不同的上下文中有所不同,但在表單構造器中,您知道上下文是表單上下文(您的主線程)。 因此,我們在Context中保存我們可以訪問表單(及其所有控件)的上下文。

添加此方法以簡化控件的工作:

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

它類似於InvokeRequired但具有SynchronizationContext 如果您在正確的位置(與表單的上下文相同),只需運行該操作。 在其他情況下,在表單上下文中運行操作。 在這里您可以看到它是使用Post完成的。 通過這種方式,您可以避免死鎖並接受代碼可能會延遲運行(因為已入隊)。

為什么是Action 因為您可以使用該操作運行任何東西,而將參數留在方法調用之外。 您不需要許多具有不同參數的方法來控制對控件的任何訪問。

例如:

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

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

您使用text參數運行AppendText ,但在RunInMainThread方法中不需要此參數。

另一個例子,設置進度條來啟動和清除列表視圖:

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

關於代表:它們就像一個合同,一個接口。 代表沒有代碼,只告訴您有關 arguments 的信息......但沒有代碼。

在您的代碼中:

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

您正在運行與您的委托匹配的實現(返回 void 並獲取 integer 作為參數)。

就好像:

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

有:

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

如您所見,您需要一個實現。 這里:

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

您需要清除列表視圖的代碼(匹配您的委托)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM