C# событие Action против Fun c<Task> исключение - PullRequest
0 голосов
/ 28 февраля 2020

Имея следующий фрагмент кода, почему следующие подписи для события ведут себя по-разному?

Этот выдает исключение:

    public event Action SomeEvent;

Этот не выдает исключение:

    public event Func<Task> SomeEvent;

Это код для консольного приложения. Использование варианта Action для объявления события останавливает приложение и показывает трассировку стека.

internal static class Program
{
    private static void Main()
    {
        var watcher = new Watcher();
        watcher.Context.RaiseSomeEvent();

        Console.ReadKey();
    }
}

internal class Watcher
{
    public Context Context { get; } = new Context();

    public Watcher()
    {
        Context.SomeEvent += async () =>
        {
            Console.WriteLine($"Sleeping, current thread {Thread.CurrentThread.ManagedThreadId}");
            await Task.Run(() => Thread.Sleep(1000));
            Console.WriteLine($"Slept, current thread {Thread.CurrentThread.ManagedThreadId}");
            throw new Exception();
        };
    }
}

internal class Context
{
    public event Action SomeEvent;
    //public event Func<Task> SomeEvent;

    public void RaiseSomeEvent()
    {
        SomeEvent?.Invoke();
    }
}

У меня было некоторое чтение, например this , но я этого не сделал нашел что-нибудь, что определило c события.

Ответы [ 2 ]

2 голосов
/ 28 февраля 2020

Когда вы используете Action, вы преобразуете лямбда-выражение в async void, тогда как Func<Task> делает его async Task.

async void - это методы "выстрелить и забыть", поэтому вызывающий код никогда не узнает, что происходит с операцией, включая любые исключения, которые могут быть выданы:

Asyn c void методы имеют различную семантику обработки ошибок. Когда исключение выдается из асинхронного c Task или asyn c метода Task, это исключение захватывается и помещается в объект Task. В асинхронных c void-методах отсутствует объект Task, поэтому любые исключения, выброшенные из асинхронного c void-метода, будут вызваны непосредственно в SynchronizationContext, который был активен при запуске асинхронного c void-метода. - https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

2 голосов
/ 28 февраля 2020

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

Чтобы исправить это так что исключение наблюдается, вам придется изменить код так, чтобы ждать так:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    internal static class Program
    {
        private static async Task Main()
        {
            var watcher = new Watcher();
            await watcher.Context.RaiseSomeEvent();

            Console.ReadKey();
        }
    }

    internal class Watcher
    {
        public Context Context { get; } = new Context();

        public Watcher()
        {
            Context.SomeEvent += async () =>
            {
                Console.WriteLine($"Sleeping, current thread {Thread.CurrentThread.ManagedThreadId}");
                await Task.Run(() => Thread.Sleep(1000));
                Console.WriteLine($"Slept, current thread {Thread.CurrentThread.ManagedThreadId}");
                throw new Exception();
            };
        }
    }

    internal class Context
    {
        //public event Action SomeEvent;
        public event Func<Task> SomeEvent;

        public async Task RaiseSomeEvent()
        {
            await SomeEvent?.Invoke();
        }
    }
}
...