Почему реализация интерфейса на подклассе EventSource вызывает исключение во время выполнения?

Я пытаюсь использовать трассировку событий для Windows (ETW) в своем приложении.NET через класс EventSource, который был включен в.NET 4.5. Я подкласс EventSource как MyEventSource и пытается реализовать интерфейс IMyEventSource (для насмешливых целей) следующим образом:

public interface IMyEventSource
{
  void Test();
}

public class MyEventSource : EventSource, IMyEventSource
{
  public static MyEventSource Log = new MyEventSource();

  [Event(1)]
  public void Test()
  {
    this.WriteEvent(1);
  }
}

Когда я запускаю PerfView и выполняю этот код, я получаю IndexOutOfRangeException по вызову WriteEvent, Если я удалю интерфейс, изменив код...

public class MyEventSource : EventSource
{
  public static MyEventSource Log = new MyEventSource();

  [Event(1)]
  public void Test()
  {
    this.WriteEvent(1);
  }
}

... тогда все работает просто отлично.

Вот код, который я использовал для тестирования в обоих случаях:

static void Main(string[] args)
{
  MyEventSource.Log.Test();
}

Почему мой подкласс EventSource сломаться, если он просто реализует интерфейс?

Вот соответствующий пост.

5 ответов

Решение

На момент постановки вопроса ответ от @LarsSkovslund был правильным. Однако со стабильной версией https://www.nuget.org/packages/Microsoft.Diagnostics.Tracing.EventSourceне System.Diagnostics.Tracing.EventSource, которая встроена в.Net Framework начиная с версии 4.5), Microsoft изменила это в соответствии со своим постом в блоге:

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

Два изменения в этой области:

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

  • Представление о типе источника служебных событий (определяемом как абстрактный класс, производный от EventSource) вводится для поддержки совместного использования кода несколькими типами источников событий в проекте (например, для оптимизированных перегрузок WriteEvent()).

Хотя источник событий не может реализовать интерфейс, его можно обернуть другим классом, который это делает. (Это пример шаблона проектирования адаптера)

public class EventSourceAdapter : IEventSource
{
    private MyEventSource log;

    public EventSourceAdapter(MyEventSource log)
    {
        this.log = log;
    }

    public void Test()
    { 
        log.Test()
    }
}
} 

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

Вы получаете IndexOutOfRangeException, потому что WriteEvent будет использовать параметр идентификатора события, чтобы искать блок дескриптора с индексом, совпадающим с идентификатором события, таким образом, создавая исключение, когда индекс не существует.

Короче говоря, DONT использовал интерфейсы для определения событий ETW с помощью EventSource.

Ура Ларс

На сегодняшний день (29 сентября 2014 г.) код, предоставленный исходным плакатом, не работает с собственным кодом, поставляемым с.NET 4.5. Он по-прежнему генерирует исключение "IndexOutOfRange" и, по его словам, делает это только в том случае, если отслеживаются события ETW (я использую PerfView).

Тем не менее, я проверил.NET версии 4.0 с использованием библиотеки Microsoft EventSource из nuget.org, и его код работает с этим.

Затем я установил Microsoft EventSource Library из nuget в проект.NET версии 4.5. Я удостоверился, что унаследован от Microsoft.Diagnostics.Tracing.EventSource, а не от System.Diagnostics.Tracing.EventSource из нативной библиотеки.NET 4.5. Это сработало, но я также обнаружил, что должен пометить те методы, которые унаследованы от интерфейса, с помощью атрибута [Microsoft.Diagnostics.Tracing.Event(int)].

Я также наблюдал некоторые странные поведения, которые я не мог объяснить. Иногда некоторые из моих событий отображаются в PerfView как "EventID(0)" вместо имени метода. Иногда я получал неожиданные исключения IndexOutOfRange. Как я могу догадаться, регистрация из предыдущего испытания осталась в памяти. Я начал переименовывать свой класс EventSource между испытаниями, и у меня больше не было этих проблем.

JR

Существует обходной путь для этой проблемы (извините, я не знаю, как объяснить проблему). Если вы метод украшен NonEventAttribute Вы сможете использовать свой интерфейс.

Ваш интерфейс:

public interface IMyEventSource
{
  void Test();
}

И реализация:

public class MyEventSource : EventSource, IMyEventSource
{
    public static MyEventSource Log = new MyEventSource();

    [NonEvent]
    public void Test()
    {
        this.InternalTest();
    }

    [Event(1)]
    private void InternalTest()
    {
        this.WriteEvent(1);
    }
}
Другие вопросы по тегам