4

ボタン、ラベル、進行状況バーを含むフォームがあるので、ボタンをクリックするとクラス b のインスタンスが作成され、プロセスが実行されます。プロセスが完了すると、EventHandler が呼び出され、メイン フォームのラベルに「完了」が表示されます。

これを行うために、デリゲート (SetStatus) のイベント (SetStatusEvent) を作成しました。そして、このイベントを EventHandler (usbforProcessExited) の外で呼び出すと問題ないように見えますが、usbforProcessExited から呼び出すとエラーが発生します -

object reference not set to an instance of an object

メインフォーム

public partial class main : Form
{
    b rsSet = new b();

    public main()
    {
        InitializeComponent();
        rsSet.SetStatusEvent += new RemoteS.SetStatus(updateStatus);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        rsSet.FormatUSB();
    }

    private delegate void UpdateStatus(int i, string str, Color clr);

    private void SetStatus(int i, string str, Color clr)
    {
        this.progressBar1.Value = i;
        this.lbl_status.ForeColor = clr;
        this.lbl_status.Text = str;
    }

    private void updateStatus(int i, String msg, Color color)
    {
        object[] p = GetInokerPara(i, msg, color);
        BeginInvoke(new UpdateStatus(SetStatus), p);
    }

    private object[] GetInokerPara(int progress, string msg, Color color)
    {
        object[] para = new object[3];
        para[0] = progress;
        para[1] = msg;
        para[2] = color;

        return para;
    }
}

クラスb

class b
{
    public delegate void SetStatus(int i, string msg, Color color);
    public event SetStatus SetStatusEvent;

    System.Diagnostics.Process usbfor = new System.Diagnostics.Process();

    public void FormatUSB()
    {

        usbfor.StartInfo.FileName = @"usbformat.bat";
        usbfor.EnableRaisingEvents = true;
        usbfor.Exited += new EventHandler(usbforProcessExited);
        usbfor.Start();
    }

    public void usbforProcessExited(object sender, EventArgs f)
    {
        SetStatusEvent(100, "DONE", Color.Green); //ERROR HERE! (object reference not set to an instance of an object
    }
}

問題はどこだ?

4

4 に答える 4

10

サブスクライバーがいない場合、イベントは null です。

次の 2 つの解決策があります。

  1. 宣言時にイベントを初期化します (ダミーのサブスクライバーは何もしません):

    public event SetStatus SetStatusEvent = delegate { };
    
  2. (スレッドセーフな方法で) 発生させる前に、イベントの null を確認します。

    public void usbforProcessExited(object sender, EventArgs f)
    {
        SetStatus setStatus = SetStatusEvent;
        if (setStatus != null)
        {
            setStatus(100, "DONE", Color.Green);
        }
    }
    
于 2013-07-31T07:39:54.450 に答える
4

競合状態があります:

usbforProcessExitedのコンストラクターでサブスクライブされ、 をb呼び出す前に呼び出される可能性がありますrsSet.SetStatusEvent += new RemoteS.SetStatus(updateStatus)

usbfor.Start()に加入した後にのみ電話する必要がありますSetStatusEvent

関連する問題は、イベントが別のスレッドで実行されることです。/rsSet.SynchronizingObjectを手動で呼び出さずにイベント ハンドラーがフォームを変更できるように、プロセスを開始する前に設定する必要があります。InvokeBeginInvoke

于 2013-07-31T07:34:32.647 に答える
1

nullまだ誰もサブスクライブしていない場合、イベントはです。したがって、次のnullように平等を制御することをお勧めします。

    public void usbforProcessExited(object sender, EventArgs f)
    {
        if(SetStatusEvent!=null)
            SetStatusEvent(100, "DONE", Color.Green);
    }

そのため、次の行のように、外部でうまく機能します。

 rsSet.SetStatusEvent += new RemoteS.SetStatus(updateStatus);

イベントの購読と初期化。

内部から呼び出すと、サブスクリプションは作成されないため、イベントはnull.

編集

コメントに従って、イベントの null 参照チェックを処理する、よりスレッド セーフなアプローチを提供しましょう。

   public void usbforProcessExited(object sender, EventArgs f)
    {
        var ev = SetStatusEvent; //[1]
        if(ev!=null) //[2]
            ev(100, "DONE", Color.Green);
    }

代入操作は CLR ではアトミックであるため、行 [1] と [2] の間であっても、他の誰かがイベントを null にリセットしても、あなたのイベントevは引き続き有効であり、コードはクラッシュすることなく実行されます。これが望ましい動作であるかどうかは、具体的なケースに依存するため、これはスレッドセーフな方法でイベントの null 参照制御を管理するための別のオプションです。

于 2013-07-31T07:33:36.433 に答える