引用Winform传递Hashtable;在 BGW 中使用此变量在取消 BGW 时不更新最初传递的哈希表
Pass Hashtable by reference to Winform; usage of this variable in BGW not updating originally passed hashtable when canceling BGW
我想将哈希表传递给 winform 并使用后台工作程序向哈希表添加一些值。如果我取消 BGW 操作,哈希表应该恢复到它的原始值(即在 BGW 启动之前)。
使用 BGW 中的以下代码 SomeHashtable returns,在取消之前添加了值,即使我在取消时尝试将其重置为 RunWorkerCompleted 中的原始状态。我需要帮助来理解这里的问题以及如何正确实施重置。
我在下面准备了一个简短的示例,其中包含一个带有两个按钮(“添加”和“取消”)的表单。
Module Module1
Sub Main()
Dim SomeHashtable As New Hashtable()
SomeHashtable.Add(-1, -1)
Dim dlgAdd As New frmFileAdd(SomeHashtable)
dlgAdd.ShowDialog()
System.Diagnostics.Debug.Print(SomeHashtable.Count) 'if user cancels BGW this should be 1 (-1,-1)
End Sub
End Module
Public Class frmFileAdd
Private SomeHashtableForm As Hashtable = Nothing
Private SomeHashtableInit As Hashtable = Nothing
Public Sub New(ByRef _SomeHashtable As Hashtable)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
SomeHashtableForm = _SomeHashtable
SomeHashtableInit = SomeHashtableForm.Clone() 'shallow copy to reset SomeHashtable to its initial values if i cancel the BGW
End Sub
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
bgwAdd.RunWorkerAsync()
End Sub
Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
If bgwAdd.IsBusy() And Not bgwAdd.CancellationPending Then
bgwAdd.CancelAsync()
Else
Me.Close()
End If
End Sub
Private Sub bgwAdd_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bgwAdd.DoWork
For iRun = 0 To 9
If Not bgwAdd.CancellationPending Then
SomeHashtableForm.Add(iRun, iRun)
Thread.Sleep(1000)
Else
e.Cancel = True
Exit For
End If
Next
End Sub
Private Sub bgwAddFiles_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgwAdd.RunWorkerCompleted
If e.Cancelled Then
SomeHashtableForm = SomeHashtableInit 'I was expecting that this will also affect the ByRef _SomeHashtable variable used in the constructor but it is not
'SomeHashtableForm = New Hashtable(SomeHashtableInit) i also tried this
Me.Close()
Else
'Do something else
End If
End Sub
End Class
正如我在评论中提到的,很明显您并不真正了解值类型和引用类型或按值和按引用传递的实际工作原理。这里:
Public Sub New(ByRef _SomeHashtable As Hashtable)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
SomeHashtableForm = _SomeHashtable
SomeHashtableInit = SomeHashtableForm.Clone() 'shallow copy to reset SomeHashtable to its initial values if i cancel the BGW
End Sub
那个ByRef
根本没有用。通过引用传递引用类型 (class) 的唯一方法是在该方法内将不同的对象分配给该参数。您没有为该构造函数中的参数分配任何内容,因此声明它 ByRef
毫无意义。此外,您绝对不应该声明构造函数参数 ByRef
。构造函数的目的是构造和对象,而不是传递数据。
这里:
If e.Cancelled Then
SomeHashtableForm = SomeHashtableInit
您所做的只是将表单的一个字段分配给另一个字段。这对表格以外的任何东西的影响完全为零。这两个字段都是私有的,不会以任何方式公开,因此无法从表单外部访问它们。
你究竟应该做什么还有待商榷,因为你能做的绝对不止一件事。我建议你应该做的是在 RunWorkerCompleted
事件之前根本不要触摸原始词典。只有在那个时候你才应该填充原始字典,当且仅当工作没有被取消时。这可能看起来像这样:
Module Module1
Sub Main()
Dim data As New Dictionary(Of Integer, Integer) From {{-1, -1}}
Using dialogue As New Form1(data)
If dialogue.ShowDialog() = DialogResult.OK Then
Console.WriteLine($"Operation completed. Item count = {data.Count}")
Else
Console.WriteLine($"Operation cancelled. Item count = {data.Count}")
End If
End Using
End Sub
End Module
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private data As Dictionary(Of Integer, Integer)
Public Sub New(data As Dictionary(Of Integer, Integer))
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.data = data
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
BackgroundWorker1.CancelAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim tempData As New Dictionary(Of Integer, Integer)
For i = 0 To 9
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
Return
End If
tempData.Add(i, i)
Thread.Sleep(1000)
Next
e.Result = tempData
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If e.Cancelled Then
DialogResult = DialogResult.Cancel
Else
Dim tempData = DirectCast(e.Result, Dictionary(Of Integer, Integer))
For Each kvp In tempData
data.Add(kvp.Key, kvp.Value)
Next
DialogResult = DialogResult.OK
End If
End Sub
End Class
在这种情况下,数据被添加到一个完全独立的 Dictionary
中,并且仅在工作完成时才转移到原始文件中,而不会被取消。请注意,在实际情况下,这可能意味着您最终会得到重复的密钥。为避免这种情况,您应该在添加到临时 Dictionary
之前检查原始 Dictionary
:
If data.ContainsKey(i) Then
'This key can't be added to the original data so decide what to do about it.
Else
tempData.Add(i, i)
End If
我想将哈希表传递给 winform 并使用后台工作程序向哈希表添加一些值。如果我取消 BGW 操作,哈希表应该恢复到它的原始值(即在 BGW 启动之前)。
使用 BGW 中的以下代码 SomeHashtable returns,在取消之前添加了值,即使我在取消时尝试将其重置为 RunWorkerCompleted 中的原始状态。我需要帮助来理解这里的问题以及如何正确实施重置。
我在下面准备了一个简短的示例,其中包含一个带有两个按钮(“添加”和“取消”)的表单。
Module Module1
Sub Main()
Dim SomeHashtable As New Hashtable()
SomeHashtable.Add(-1, -1)
Dim dlgAdd As New frmFileAdd(SomeHashtable)
dlgAdd.ShowDialog()
System.Diagnostics.Debug.Print(SomeHashtable.Count) 'if user cancels BGW this should be 1 (-1,-1)
End Sub
End Module
Public Class frmFileAdd
Private SomeHashtableForm As Hashtable = Nothing
Private SomeHashtableInit As Hashtable = Nothing
Public Sub New(ByRef _SomeHashtable As Hashtable)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
SomeHashtableForm = _SomeHashtable
SomeHashtableInit = SomeHashtableForm.Clone() 'shallow copy to reset SomeHashtable to its initial values if i cancel the BGW
End Sub
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
bgwAdd.RunWorkerAsync()
End Sub
Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
If bgwAdd.IsBusy() And Not bgwAdd.CancellationPending Then
bgwAdd.CancelAsync()
Else
Me.Close()
End If
End Sub
Private Sub bgwAdd_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bgwAdd.DoWork
For iRun = 0 To 9
If Not bgwAdd.CancellationPending Then
SomeHashtableForm.Add(iRun, iRun)
Thread.Sleep(1000)
Else
e.Cancel = True
Exit For
End If
Next
End Sub
Private Sub bgwAddFiles_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgwAdd.RunWorkerCompleted
If e.Cancelled Then
SomeHashtableForm = SomeHashtableInit 'I was expecting that this will also affect the ByRef _SomeHashtable variable used in the constructor but it is not
'SomeHashtableForm = New Hashtable(SomeHashtableInit) i also tried this
Me.Close()
Else
'Do something else
End If
End Sub
End Class
正如我在评论中提到的,很明显您并不真正了解值类型和引用类型或按值和按引用传递的实际工作原理。这里:
Public Sub New(ByRef _SomeHashtable As Hashtable)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
SomeHashtableForm = _SomeHashtable
SomeHashtableInit = SomeHashtableForm.Clone() 'shallow copy to reset SomeHashtable to its initial values if i cancel the BGW
End Sub
那个ByRef
根本没有用。通过引用传递引用类型 (class) 的唯一方法是在该方法内将不同的对象分配给该参数。您没有为该构造函数中的参数分配任何内容,因此声明它 ByRef
毫无意义。此外,您绝对不应该声明构造函数参数 ByRef
。构造函数的目的是构造和对象,而不是传递数据。
这里:
If e.Cancelled Then
SomeHashtableForm = SomeHashtableInit
您所做的只是将表单的一个字段分配给另一个字段。这对表格以外的任何东西的影响完全为零。这两个字段都是私有的,不会以任何方式公开,因此无法从表单外部访问它们。
你究竟应该做什么还有待商榷,因为你能做的绝对不止一件事。我建议你应该做的是在 RunWorkerCompleted
事件之前根本不要触摸原始词典。只有在那个时候你才应该填充原始字典,当且仅当工作没有被取消时。这可能看起来像这样:
Module Module1
Sub Main()
Dim data As New Dictionary(Of Integer, Integer) From {{-1, -1}}
Using dialogue As New Form1(data)
If dialogue.ShowDialog() = DialogResult.OK Then
Console.WriteLine($"Operation completed. Item count = {data.Count}")
Else
Console.WriteLine($"Operation cancelled. Item count = {data.Count}")
End If
End Using
End Sub
End Module
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private data As Dictionary(Of Integer, Integer)
Public Sub New(data As Dictionary(Of Integer, Integer))
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.data = data
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
BackgroundWorker1.CancelAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim tempData As New Dictionary(Of Integer, Integer)
For i = 0 To 9
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
Return
End If
tempData.Add(i, i)
Thread.Sleep(1000)
Next
e.Result = tempData
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If e.Cancelled Then
DialogResult = DialogResult.Cancel
Else
Dim tempData = DirectCast(e.Result, Dictionary(Of Integer, Integer))
For Each kvp In tempData
data.Add(kvp.Key, kvp.Value)
Next
DialogResult = DialogResult.OK
End If
End Sub
End Class
在这种情况下,数据被添加到一个完全独立的 Dictionary
中,并且仅在工作完成时才转移到原始文件中,而不会被取消。请注意,在实际情况下,这可能意味着您最终会得到重复的密钥。为避免这种情况,您应该在添加到临时 Dictionary
之前检查原始 Dictionary
:
If data.ContainsKey(i) Then
'This key can't be added to the original data so decide what to do about it.
Else
tempData.Add(i, i)
End If