Почему реализация интерфейса на подклассе 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);
}
}