C# 如何将事件参数的属性与异步方法一起使用?
如果我们想将异步方法中的属性写入控件,那么如何在异步方法中使用属性。当尝试在不调用的情况下写入时,它会引发异常。所以我想在计数器类而不是Form1中处理调用问题。我已经为这个问题写了一个例子。在此代码文本框中。文本行引发跨线程异常C# 如何将事件参数的属性与异步方法一起使用?,c#,C#,如果我们想将异步方法中的属性写入控件,那么如何在异步方法中使用属性。当尝试在不调用的情况下写入时,它会引发异常。所以我想在计数器类而不是Form1中处理调用问题。我已经为这个问题写了一个例子。在此代码文本框中。文本行引发跨线程异常 internal class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(o
internal class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Counter myCounter = new Counter(1000);
myCounter.IndexValueChanged += myCounter_IndexValueChanged;
myCounter.StartCountAsync();
}
void myCounter_IndexValueChanged(object sender, IndexValueChangedEventArgs e)
{
textBox1.Text = e.Index.ToString();
}
}
class Counter
{
public delegate void IndexValueChangedEventHandler(object sender, IndexValueChangedEventArgs e);
public event IndexValueChangedEventHandler IndexValueChanged;
int _maxNumber;
int _index;
public Counter(int maxNumber)
{
_maxNumber = maxNumber;
}
public async void StartCountAsync()
{
await Task.Run(() =>
{
for (int i = 0; i < _maxNumber; i++)
{
_index = i;
if (IndexValueChanged != null)
IndexValueChanged(this, new IndexValueChangedEventArgs(_index));
Thread.Sleep(100);
}
});
}
}
class IndexValueChangedEventArgs
{
int indexNum;
public IndexValueChangedEventArgs(int index)
{
indexNum = index;
}
public int Index
{
get { return indexNum; }
}
}`
内部类表单1:表单
{
公共表格1()
{
初始化组件();
}
私有无效按钮1\u单击(对象发送者,事件参数e)
{
计数器myCounter=新计数器(1000);
myCounter.IndexValueChanged+=myCounter\u IndexValueChanged;
myCounter.StartCountAsync();
}
void myCounter_IndexValueChanged(对象发送方,IndexValueChangedEventArgs e)
{
textBox1.Text=e.Index.ToString();
}
}
班级计数器
{
公共委托无效IndexValueChangedEventHandler(对象发送者,IndexValueChangedEventArgs e);
公共事件指数价值变更Handler指数价值变更;
int_maxNumber;
int_指数;
公共计数器(int maxNumber)
{
_maxNumber=maxNumber;
}
公共异步void StartCountAsync()
{
等待任务。运行(()=>
{
对于(int i=0;i<\u maxNumber;i++)
{
_指数=i;
如果(IndexValueChanged!=null)
IndexValueChanged(这是新的IndexValueChangedEventArgs(_索引));
睡眠(100);
}
});
}
}
类索引ValueChangedEventArgs
{
int indexNum;
公共索引VALUECHANGEDVENTARGS(整数索引)
{
indexNum=指数;
}
公共整数索引
{
获取{return indexNum;}
}
}`
您可以更改应用程序的行为,以允许跨线程调用修改表单元素:
System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false;
或者,如果人们说这是不推荐的、不安全的、不专业的
您可以在计数器类中使用。WebClient.DownloadFileAsync方法在内部使用相同的方法。这将确保在UI线程上调用事件处理程序
这是最后一个计数器类,它应该在UI线程上调用委托
class Counter
{
public delegate void IndexValueChangedEventHandler(object sender, IndexValueChangedEventArgs e);
public event IndexValueChangedEventHandler IndexValueChanged;
int _maxNumber;
int _index;
public Counter(int maxNumber)
{
_maxNumber = maxNumber;
}
public async void StartCountAsync()
{
AsyncOperation asyncCountOperation = AsyncOperationManager.CreateOperation(null);
await Task.Run(() =>
{
for (int i = 0; i < _maxNumber; i++)
{
_index = i;
asyncCountOperation.Post(new SendOrPostCallback(InvokeDelegate), _index);
Thread.Sleep(100);
}
});
}
private void InvokeDelegate(object index)
{
if (IndexValueChanged != null)
{
IndexValueChanged(this, new IndexValueChangedEventArgs((int)index));
}
}
}
类计数器
{
公共委托无效IndexValueChangedEventHandler(对象发送者,IndexValueChangedEventArgs e);
公共事件指数价值变更Handler指数价值变更;
int_maxNumber;
int_指数;
公共计数器(int maxNumber)
{
_maxNumber=maxNumber;
}
公共异步void StartCountAsync()
{
AsyncOperation asyncCountOperation=AsyncOperationManager.CreateOperation(null);
等待任务。运行(()=>
{
对于(int i=0;i<\u maxNumber;i++)
{
_指数=i;
asyncCountOperation.Post(新的SendOrPostCallback(InvokeDelegate))\u索引;
睡眠(100);
}
});
}
私有void InvokeDelegate(对象索引)
{
如果(IndexValueChanged!=null)
{
IndexValueChanged(这是新的IndexValueChangedEventArgs((int)索引));
}
}
}
要在ui线程上调用委托,您需要一个windows句柄。我不知道为什么要在Counter类中进行调用,但在我看来,最简单的方法是:给您的Counter实例一个对表单的引用,并在该引用上调用。(从我的手机上写,所以很难添加代码示例,明天将进行编辑)
编辑:但我真的认为表单的责任是检查它是否在正确的线程中进行了更改,以及fore调用(Begin)调用本身
编辑:这是添加了“父”引用的计数器类:
class Counter
{
public delegate void IndexValueChangedEventHandler(object sender, IndexValueChangedEventArgs e);
public event IndexValueChangedEventHandler IndexValueChanged;
int _maxNumber;
int _index;
Control _parent;
public Counter(Control parent, int maxNumber)
{
_maxNumber = maxNumber;
_parent = parent;
}
public async void StartCountAsync()
{
await Task.Run(() =>
{
for (int i = 0; i < _maxNumber; i++)
{
_index = i;
// introduce local variable for thread safety
IndexValueChangedEventHandler handler = IndexValueChanged;
if (handler != null)
{
if (_parent == null || !_parent.InvokeRequired)
handler(this, new IndexValueChangedEventArgs(_index));
else
// use BeginInvoke
_parent.BeginInvoke(handler, this, new IndexValueChangedEventArgs(_index));
}
Thread.Sleep(100);
}
});
}
}
最好在Counter类中使用BeginInvoke,因为Invoke将等待UI线程执行委托,因此计数器的计数可能比预期的慢(或者web客户端使用网络资源的时间比需要的时间长)。希望这对你有帮助
编辑:引入局部变量“handler”。否则,消费者可能会在您对事件进行null检查后注销该事件,并且在您调用它时该事件为null。在您的特定示例中,首先使用后台线程对我来说没有意义。当然,如果您在UI线程上调用
Thread.Sleep
,这显然是不好的,但是您不需要调用Thread.Sleep
来获得要等待的async
方法
public async void StartCountAsync()
{
for (int i = 0; i < _maxNumber; i++)
{
_index = i;
if (IndexValueChanged != null)
IndexValueChanged(this, new IndexValueChangedEventArgs(_index));
await Task.Delay(100);
}
}
public async void StartCountAsync()
{
对于(int i=0;i<\u maxNumber;i++)
{
_指数=i;
如果(IndexValueChanged!=null)
IndexValueChanged(这是新的IndexValueChangedEventArgs(_索引));
等待任务。延迟(100);
}
}
如果从UI线程调用,那么在延迟之后,此方法将继续在UI线程上执行,因此调用
IndexValueChanged
将正常工作。但是人们说这种方式不推荐,不安全,也不专业。WebClient.DownloadFileAsync方法是否使用相同的方法?因为它具有相同的progress属性,可以在Form类中直接用作e.CurrentProgress而无需调用。我查阅了您的建议大约10分钟,但代码示例非常复杂。变量之间有很多关系。我试图将代码复制到IDE,但出现了太多问题。如果你能为我的代码实现这个解决方案,它将对我帮助很大。“Valla anlamadım hocam:)”“您可以更改应用程序的行为以允许跨线程调用”——不,您不能。您可以关闭对程序中bug的检测,但它们仍然是bug。关闭检测只意味着你会得到无声的细微破坏,这将成为调试的巨大痛苦。@hvd你称之为“bug”的是什么?你是说,从创建实例的线程以外的线程调用实例方法是一个bug吗?@OguzOzgul是的。一个例外是,如果您手动创建一些
public async void StartCountAsync()
{
for (int i = 0; i < _maxNumber; i++)
{
_index = i;
if (IndexValueChanged != null)
IndexValueChanged(this, new IndexValueChangedEventArgs(_index));
await Task.Delay(100);
}
}