К сожалению, Вы ошибаетесь, lock(this) есть и он нужен (в многопоточной среде). Посмотрите на атрибуты акцессоров событий: они отмечены [MethodImpl(MethodImplOptions.Synchronized)], который эквивалентен оборачиванию тела метода в lock(this) { }, однако в C# 4.0 генерируемый код для акцессоров field-like событий изменился, теперь там lock-free реализация подписки/отписки. Не смотря на на то, что делегаты — ref-типы, при подписке два потока могут забрать одну и ту же ссылку, а потом записать свои скомбинированные через Delegate.Combine() делегаты поверх друг друга, поэтому применение += к делегатам не является потокобезопасной операцией.
Смотрю рефлектором и не вижу того, о чём вы говорите. То есть в принципе вы видимо правы, но что-то я с ходу описаной реализации события не нашёл. События FileSystemWatcher, 3.5SP1 есть что.
Определите своё field-like событие и оно там будет, в BCL многие события описаны вручную и не используют синхронизацию, так как по сути многие из них описаны в классам, которые не являются потокобезпасными по спецификации, и им эта синхронизация в акцессорах нафиг не нужна. В случае field-like событий, компилятор C#, по сути, не спрашивая Вас эмитит синхронизацию.
а можно просто отписываться от событий тогда когда подписчик в них больше не нуждается? Ежели система без четкого определения жизненного цикла объектов, то никакие костыли и заплатки ей не помогут.
К сожалению, действительно всё же существуют ситуации, когда момент отписки не может быть детерминирован и в этих случаях weak events — единственный способ исключить утечки памяти. К примеру, WPF имеет специальные средства для работы со слабыми событиями (см. наследников System.Windows.WeakEventManager), только не сказать что они выглядят удобными :)
Слабые события в C#