[英]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.