2014-02-06 2 views
1

다음 문제를 해결하는 데 어려움이 있지만 매우 힘듭니다. 버튼을 클릭 할 때 대리인을 호출하는 응용 프로그램이 있습니다. 그것은 다음과 같습니다대리자가 콜백에 반환 값을 반환했습니다.

private void button1_Click(object sender, EventArgs e) 
    { 
     MyTaskWorkerDelegate worker = new MyTaskWorkerDelegate(returnANumber); 
     AsyncCallback completedCallback = new AsyncCallback(DelegateMethod); 

     object[] myArray = new object[1]; 
     myArray[0] = "The number is: "; 

     worker.BeginInvoke(completedCallback,myArray); 
    } 
수 100 그것은 다음과 같습니다 는 "returnANumber가"는 다음 반환 1 초 잠을하도록되어있어서,

: 수면은

private int returnANumber() 
    { 
     Thread.Sleep(1000); 
     return 100; 
    } 

이상이며,

때 번호가 반환 된 까다로운 부분이 온다. click 함수에서 생성 된 배열은 콜백 메소드에 전달됩니다. 이 메서드는 배열의 문자열과 대리자에서 반환 한 번호를 나타내는 MessageBox를 표시합니다.

내가 대리자 메서드에서 반환됩니다 값에 액세스 할 수있는 방법을 알아 ... 나를 :(도와주세요 어차피
private void DelegateMethod(IAsyncResult arr) 
    { 
     object[] myArray = (object[])arr.AsyncState; 

     //Messagebox should show "The number is: 100" 
     MessageBox.Show(myArray[0].ToString() + theNumberFromTheMethos); 
    } 

+2

당신은 그 모든 것을'Task.Run()'으로 대체해야합니다. 이것은 사용하기 쉽고 _far_ 사용하기 쉽습니다. – SLaks

+1

태그를 편집하고 [asp.net]을 (를) 제거했습니다. WinForms 앱, 즉 MessageBox.Show와'button1_Click '을 사용하는 방식이라고 가정합니다. – Noseratio

답변

1

당신은을 통해 반환 값에 액세스 할 수 있습니다 기능은 다음과 같습니다 MyTaskWorkerDelegate.EndInvoke은. 따라서, 당신은 MyTaskWorkerDelegate에 대한 참조를 유지하고 DelegateMethod로 전달해야 할 것이다. 코드는 현재 보이는 방법은, 당신이 IAsyncResult.AsyncState를 통해 모두 myArrayMyTaskWorkerDelegate을 통과하기 위해 별도의 객체가 필요 것입니다.

당신은 피할 수 티 모자를 사용하고 익명의 lambda/delegate를 사용하고 지역 변수에 액세스하는 경우 코드를 크게 단순화합니다. C# 컴파일러가 상태를 유지하는 마술을 할 것입니다 : 콜백이 호출 될 때

private void button1_Click(object sender, EventArgs e) 
{ 
    MyTaskWorkerDelegate worker = new MyTaskWorkerDelegate(returnANumber); 

    object[] myArray = new object[1]; 
    myArray[0] = "The number is: "; 

    AsyncCallback completedCallback = new AsyncCallback((ar) => 
    { 
     var result = worker.EndInvoke(ar); 

     // you cannot use MessageBox here, you're on a non-UI random pool thread 
     Debug.Print(myArray[0].ToString() + result); 
    }); 

    worker.BeginInvoke(completedCallback, null); // no need to pass the state 
} 

또 다른 점은, 당신은 당신의 AsyncCallback 대리자 MessageBox을 사용할 수 없습니다, 당신은 임의의 풀 스레드에있어. 당신은 그것을 위해 Control.BeginInvoke를 사용해야하는 것 :

AsyncCallback completedCallback = new AsyncCallback((ar) => 
{ 
    var result = worker.EndInvoke(ar); 

    // you cannot use MessageBox here, you're on a non-UI random pool thread 
    this.BeginInvoke(new MethodInvoker(() => 
    { 
     // here you can use MessageBox, you're on the UI thread 
     MessageBox.Show(myArray[0].ToString() + result); 
    })); 
}); 

주, Control.BeginInvoke의 목적은 이름의 유사성에도 불구하고, Delegate.BeginInvoke에서 compeltely 다르다. WinForms UI 스레드에서 비동기 적으로 콜백을 호출하는 데 사용됩니다. @SLaks 지적대로 .NET 4.5 (또는 + Microsoft.Bcl.Async 및 VS2012 + .NET 4.0)을 사용할 수있는 경우, 더 TPLasync/await과 코드 이벤트를 단순화 할 수 말했다

. Thread.Sleep(1000)에 대한 유일한 이유가 일시 정지 인 경우

private int returnANumber() 
{ 
    Thread.Sleep(1000); 
    return 100; 
} 

private async void button1_Click(object sender, EventArgs e) 
{ 
    object[] myArray = new object[1]; 
    myArray[0] = "The number is: "; 

    int result = await Task.Run(() => returnANumber()); 
    MessageBox.Show(myArray[0].ToString() + result); 
} 

또한, 대신 Task.Delay를 사용할 수 있습니다 :

private async Task<int> returnANumber() 
{ 
    await Task.Delay(1000); 
    return 100; 
} 

private async void button1_Click(object sender, EventArgs e) 
{ 
    object[] myArray = new object[1]; 
    myArray[0] = "The number is: "; 

    int result = await returnANumber(); 
    MessageBox.Show(myArray[0].ToString() + result); 
} 

이 방법, 당신은 명시 적으로하지 않습니다 코드는 다음과 같이 간단 할 수도 풀 스레드를 사용합니다.

+1

답변 해 주셔서 대단히 감사합니다. TPL 솔루션은 응용 프로그램이 .NET 4.5에 내장되어 있으므로 사용하고 있습니다. 이것은 훨씬 쉽습니다! 다시 한 번 감사드립니다! – colosso

+1

@colosso, 아무 문제 없어, 다행히 도왔다. TPL/async/await로 계속 진행한다면 http : // stackoverflow 링크를 확인하십시오.co.kr/tags/async-await/info – Noseratio

관련 문제