Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/.htaccess/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何将事件参数的属性与异步方法一起使用?_C# - Fatal编程技术网

C# 如何将事件参数的属性与异步方法一起使用?

C# 如何将事件参数的属性与异步方法一起使用?,c#,C#,如果我们想将异步方法中的属性写入控件,那么如何在异步方法中使用属性。当尝试在不调用的情况下写入时,它会引发异常。所以我想在计数器类而不是Form1中处理调用问题。我已经为这个问题写了一个例子。在此代码文本框中。文本行引发跨线程异常 internal class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(o

如果我们想将异步方法中的属性写入控件,那么如何在异步方法中使用属性。当尝试在不调用的情况下写入时,它会引发异常。所以我想在计数器类而不是Form1中处理调用问题。我已经为这个问题写了一个例子。在此代码文本框中。文本行引发跨线程异常

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