2013-07-31 3 views
3

버튼을 클릭하면 프로세스를 실행하는 클래스 b의 인스턴스를 만들 수 있도록 버튼, 레이블 및 진행률 막대가있는 양식이 있습니다. 프로세스가 완료되면 EventHandler를 호출하여 메인 폼의 레이블에 "done"을 표시합니다!이벤트가 null이되는 이유는 무엇입니까? (객체 참조가 객체의 인스턴스로 설정되지 않음)

이렇게하려면 대리인 (SetStatus)의 이벤트 (SetStatusEvent)를 만들었습니다. 그리고이 이벤트 핸들러 (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 
    } 
} 

어디에서 문제가 발생합니까?

+0

당신은 최소한의 작업 예제를 생산하기 위해 배워야한다. 귀하의 코드에는이 질문과 관련없는 많은 내용이 포함되어 있습니다. – CodesInChaos

+0

문제의 원인이라고 생각하지 않지만 Exited 이벤트를 발생시키는 프로세스에 "usbfor.EnableRaisingEvents = true"가 필요하지 않습니까? –

+0

@ChrisSpicer OPS! 실제로 나는 그 선을 가지고있다! 나는 방금 게시 한 코드의 양을 최소화하려고 노력했다. 그 부분을 언급 해 주셔서 감사합니다. – daygoor

답변

3

당신은 경쟁 조건을 가지고있다.

SetStatusEvent을 구독하면 usbfor.Start()으로 전화해야합니다.

관련된 문제는 이벤트가 다른 스레드에서 실행된다는 것입니다. 이벤트 처리기가 Invoke/BeginInvoke을 수동으로 호출하지 않고 양식을 수정할 수 있도록 프로세스를 시작하기 전에 rsSet.SynchronizingObject을 설정해야합니다.가입자가없는 경우

1

아무도 아직 구독하지 않은 경우 이벤트는 null입니다. 는 그래서 같은 null 평등 제어하는 ​​좋은 방법입니다 :

외부 가 잘 작동 이유를 가지고,의
public void usbforProcessExited(object sender, EventArgs f) 
    { 
     if(SetStatusEvent!=null) 
      SetStatusEvent(100, "DONE", Color.Green); 
    } 

,이 라인 :

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

때문에 가입과의 initilizaton 행사.

에서으로 전화하면 구독이 없기 때문에 이벤트는 null입니다.

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

그래서 짝수 라인 [사이에서 CLR에서 원자 그 할당 작업 아이오와를, 기억의 이벤트에 null 참조 확인을 처리하는 더 많은 스레드 안전한 접근을 제공 할 의견에 따라

편집

1] 및 [2] 다른 사람이 이벤트를 null로 재설정하면 ev은 여전히 ​​유효하며 코드는 충돌하지 않고 실행됩니다. 이것이 바람직한 동작인지 아니면 구체적인 케이스에 따라 다르므로, 이것은 thread-safe 방식으로 이벤트에 대한 null 참조 제어를 관리하는 또 다른 옵션입니다.

usbforProcessExited

b의 생성자에 가입됩니다 그리고 당신이 rsSet.SetStatusEvent += new RemoteS.SetStatus(updateStatus)를 호출하기 전에 호출 될 수 있습니다

+2

스레드 안전하지 않은 경우를 제외하고 마지막 가입자가 수표와 호출을 취소 한 경우 NullReferenceException이 발생합니다. 어떤 스레드를 지원 하느냐에 따라 문제가 될 수도 있고 아닐 수도 있습니다. –

+0

일반적으로'var handler = SetStatusEvent;'를 먼저 수행합니다 (이유에 대한 참조를 찾을 수는 없지만). – Default

+0

null 체크는 OP 코드에서 손실 이벤트를 발생시킵니다. 그래서 여기 해결책이 아닙니다. – CodesInChaos

6

이벤트는, null입니다. (더미 가입자가 아무것도하지 않고) 선언 할 때

  1. 이벤트를 초기화 :

    public event SetStatus SetStatusEvent = delegate { }; 
    
  2. 스레드 안전한 방법으로 (올리기 전에 널위한 이벤트를 확인

    는 두 가지 해결책이 있습니다) :

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

null 체크 (올바르게 완료된 경우에도)는 OP 코드에서 손실 된 이벤트로 연결됩니다. 그래서 여기 해결책이 아닙니다. – daygoor

+0

이 경우 Null 검사는 증상 만 숨 깁니다. –

+0

@daygoor Raise 이벤트 메소드를 작성할 때이 패턴을 사용해야합니다. 당신이 당신 자신의 말로 말했듯이 (당신이 OP이기 때문에 흥미 롭습니다), "올바르게 끝났을 때". – Default

1

존 소총은 # 6.0 c를 당신은 또한 사용할 수에서 저를 가르쳤다 :

SetStatusEvent?.Invoke(100, "DONE", Color.Green);;