Exception handling when implementing the APM pattern with AsyncEnumerator
I'm trying to implement the APM pattern using Richter's AsyncEnumerator
class. The goal is to implement an ExtendedSocket
class which is derived from Socket
and offers Begin/EndReceiveFixed
and Begin/EndSendFixed
methods to send or receive a fixed amount of bytes asynchronously.
The code looks like this (I omitted the sending part since it is basically the same as for receiving):
class ExtendedSocket : Socket
{
public Exten开发者_开发知识库dedSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
: base(addressFamily, socketType, protocolType)
{
}
public IAsyncResult BeginReceiveFixed(byte[] buffer, SocketFlags socketFlags, AsyncCallback callback, Object state)
{
AsyncEnumerator ae = new AsyncEnumerator();
return ae.BeginExecute(DoReceiveFixed(ae, buffer, socketFlags), callback, state);
}
public void EndReceiveFixed(IAsyncResult asyncResult)
{
AsyncResult ar = asyncResult as AsyncResult;
(ar.InitiatingObject as AsyncEnumerator).EndExecute(ar);
}
private IEnumerator<Int32> DoReceiveFixed(AsyncEnumerator ae, byte[] buffer, SocketFlags socketFlags)
{
int totalReceivedBytes = 0;
while (totalReceivedBytes < buffer.Length)
{
BeginReceive(buffer, totalReceivedBytes, buffer.Length - totalReceivedBytes, socketFlags, ae.End(), null);
yield return 1;
totalReceivedBytes += EndReceive(ae.DequeueAsyncResult());
}
}
}
This works perfectly fine in my application but I don't know how to handle exceptions in DoReceiveFixed
. I'd like to implement the default APM behaviour where exceptions are (re)thrown when EndReceiveFixed
is called.
Unfortunately I don't have access to the AsyncResult
object inside DoReceiveFixed
, so I can't call SetAsCompleted
with an exception on the AsyncResult
object.
My current workaround is to use AsyncEnumerator<Exception>
instead of AsyncEnumerator
like this:
class ExtendedSocket : Socket
{
public ExtendedSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
: base(addressFamily, socketType, protocolType)
{
}
public IAsyncResult BeginReceiveFixed(byte[] buffer, SocketFlags socketFlags, AsyncCallback callback, Object state)
{
AsyncEnumerator<Exception> ae = new AsyncEnumerator<Exception>();
return ae.BeginExecute(DoReceiveFixed(ae, buffer, socketFlags), callback, state);
}
public void EndReceiveFixed(IAsyncResult asyncResult)
{
AsyncResult ar = asyncResult as AsyncResult;
AsyncEnumerator<Exception> ae = ar.InitiatingObject as AsyncEnumerator<Exception>;
ae.EndExecute(ar);
if (ae.Result != null)
{
throw ae.Result;
}
}
private IEnumerator<Int32> DoReceiveFixed(AsyncEnumerator<Exception> ae, byte[] buffer, SocketFlags socketFlags)
{
int totalReceivedBytes = 0;
Exception catchedException = null;
while (totalReceivedBytes < buffer.Length)
{
try
{
BeginReceive(buffer, totalReceivedBytes, buffer.Length - totalReceivedBytes, socketFlags, ae.End(), null);
}
catch (Exception ex)
{
catchedException = ex;
break;
}
yield return 1;
try
{
totalReceivedBytes += EndReceive(ae.DequeueAsyncResult());
}
catch (Exception ex)
{
catchedException = ex;
break;
}
}
ae.Result = catchedException;
}
}
This seems to work but I don't really like this solution. Is there a better way to do this? Maybe there is a way to get access to the AsyncResult
object from inside DoFixedReceive
?
With the help of Jeffrey Richter I solved my problem (see here):
There is no need to catch all exceptions in an iterator and rethrow them manually. AsyncEnumerator
does this for us.
But be careful with your debugger settings. I needed to uncheck the 'Enable Just My Code' setting on the general debugging page. Otherwise, if an exception occurs inside the iterator, the debugger breaks with an unhandled exception message before AsyncEnumerator
has the chance to catch the exception.
精彩评论