Подтвердить что ты не робот

Исключение из лямбда-выражений

Странный, который я еще не получил, это:

Скажем,

try
{
    stateClient.Socket.BeginSend(messagePrefixed, 0, messagePrefixed.Length,
        SocketFlags.None, ar => stateClient.Socket.EndSend(ar), stateClient);
}
catch (SocketException ex)
{
    // Handle SocketException.
}
catch (ObjectDisposedException ex)
{
    // Handle ObjectDisposedException.
}

Я не понимаю, почему выражение лямбда, которое возвращается с помощью ObjectDisposedException, не поймано!? Я шел глубже в лямбда, и я не могу это понять. Это о масштабах лямбда? Диапазонные переменные? Тема выпуска? Я знаю, что lambda не имеет многопоточности по своей природе, но, как вы видите, возвращение происходит из другого потока, созданного BeginSend. Прежде чем преобразовать реализацию в лямбда, это было нормально, когда у меня был метод AsyncCallBack, обрабатывающий EndSend.

Любая помощь оценивается. Заранее благодарю вас.

4b9b3361

Ответ 1

Вы правы, что у lamdas нет встроенной асинхронности или многопоточности, но Socket.BeginSend делает.

Что происходит, так это то, что блок try инкапсулирует вызов BeginSend. Если этот вызов завершается успешно, исключение не генерируется, и возвращаемый метод возвращается, независимо от того, что происходит в других потоках.

Если во время вызова BeginSend происходит исключение, будут вызываться блоки catch.

Однако выражение лямбда является асинхронным обратным вызовом, поэтому его не будет вызывать до конца. Это происходит в отдельной стоп-кадре в отдельном потоке, поэтому блок try там не работает.

Если вам нужна обработка ошибок для обратного вызова, вам нужно будет указать его внутри самого обратного вызова (то есть внутри лямбда).

Ответ 2

Это не связано с лямбдами. Делегат вызова BeginSend выполняется в другом потоке, поэтому исключение не выбрасывается в поток, который имеет инструкции catch и, следовательно, не обрабатывается. Поместите обработку исключений вместе с кодом для EndSend.

Для получения дополнительной информации см. http://msdn.microsoft.com/en-us/library/38dxf7kt.aspx

Ответ 3

Вызов анонимной функции, определяемой лямбдой, происходит асинхронно. К тому времени блок try будет долгой.

Вы код такой же: -

AsyncCallBack cb = delegate(AsyncCallback ar) { stateClient.Socket.EndSend(ar); }
stateClient.Socket.BeginSend(messagePrefixed, 0, messagePrefixed.Length,
   SocketFlags.None, cb, stateClient);

Теперь вы могли бы определить функцию: -

void MyCallBack(AsyncCallback ar) { stateClient.Socket.EndSend(ar); }

а затем код выше может стать: -

stateClient.Socket.BeginSend(messagePrefixed, 0, messagePrefixed.Length,
   SocketFlags.None, MyCallBack, stateClient);

В этом случае все это примерно одно и то же. Дело в том, что Try исключает ловушки, которые происходят во время номинального исполнения его тела. Тот факт, что вы определили код внутри тела в виде лямбда, не делает этот код более подверженным блоку Try как MyCallBack выше. Оба будут запускаться после функции, содержащей блок Try или, возможно, во время, но в другом потоке.

Ответ 4

Как уже говорилось в других ответах, вызов лямбда произойдет асинхронно и что причина исключения не будет.

Пример с асинхронными вызовами для чтения из файла:

File.WriteAllText("example.txt", new string('0', 2048));

Stream s = File.OpenRead("example.txt");

var buffer = new byte[1024];

Console.WriteLine(
    "Thread: {0} - Before asynch call...", 
    Thread.CurrentThread.ManagedThreadId);

s.BeginRead(
    buffer, 
    0, 
    1024, 
    ar =>
    {
        Thread.Sleep(100); // Simulate a long op
        Console.WriteLine(
            "Thread: {0} - Callback called...", 
            Thread.CurrentThread.ManagedThreadId);
    }
    , 0);

Console.WriteLine(
    "Thread: {0} - After asynch call...",
    Thread.CurrentThread.ManagedThreadId);

// Wait for callback to be executed
Thread.Sleep(2000);

Вывод будет:

Thread: 1 - Before asynch call...
Thread: 1 - After asynch call...
Thread: 3 - Callback called...

Ответ 5

Насколько я думаю, что я прав до сих пор, BeginSend никогда не вернет исключение, все исключения и результат будут повторно перенесены на метод EndSend(), поэтому я могу переместить блоки try catch.