Можно ли ограничить установку / сброс EventWaitHandle?

Я хотел бы иметь объект EventWaitHandle (например, ManualResetEvent), который может быть установлен / сброшен только из одного места, но его можно ожидать (с WaitOne()) из нескольких мест. Иными словами, я хочу иметь только один класс, который может устанавливать / сбрасывать его, в то время как все другие классы могут вызывать WaitOne () для него. Это похоже на обычное свойство "только для чтения":

private string MyReadOnlyText {get; private set;}

только для определенных методов в ManualResetEvent. Но не

private ManualResetEvent MyEvent {get; private set;}

Конечно, это не будет работать, потому что, хотя только класс владельца может создавать экземпляр этого свойства, любой другой внешний объект может изменить его с помощью MyEvent.Set/MyEvent.Reset().

Возможно ли что-то подобное? Цель этого состоит в том, чтобы предотвратить произвольные объекты в приложении, чтобы манипулировать состоянием дескриптора ожидания и быть уверенным, что это может быть сделано только из одного места.

1 ответ

Решение

В некоторой степени это кажется чем-то, что может быть решено соглашением, то есть просто предусмотреть, что потребители должны рассматривать этот дескриптор как "только для чтения". Простой анализ исходного кода должен помочь избежать злонамеренных действий.

Существует ограниченная поддержка концепции "только для реального" события, если вы выставляете объект только как WaitHandle, WaitHandle экземпляры не имеют Set() метод, так что помогает напомнить звонящим не связываться с ним.

Конечно, вызывающие могут по-прежнему приводить его к фактическому типу, и в WaitHandle это все еще позволяет манипулировать. Так что это не похоже на 100% гарантию. Но это помогло бы (и предоставило бы больше отличительных шаблонов, которые нужно искать, когда вы проводите аудит кода).

Наконец, подход "обертка", упомянутый в комментариях, также может работать. Это наиболее надежный и относительно распространенный шаблон для любого вида такого "ограничения доступа к элементам, кроме объекта-владельца".

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

Тем не менее, часто вы хотите инкапсулировать объект специальной доступности как отдельный класс, в то же время предоставляя привилегированный доступ к одному определенному классу и ограничивая доступ для всех остальных. В этом случае вы можете использовать вложенные классы для достижения этого разделения, причем внешний класс содержит привилегированный код, а вложенный класс (ы) содержит скрытый код.

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

class A
{
    public class B
    {
        protected ManualResetEvent _event = new ManualResetEvent(false);

        public void Wait() { _event.Wait(); }
    }

    private class C : B
    {
        public void Set() { _event.Set(); }
    }

    public B GetAnInstanceofB() { return new C(); }

    private void DoSomethingWithB(B b) { ((C)b).Set(); }
}

Теперь звонящие могут звонить B.Wait() на возвращенном экземпляре, но они не могут привести к типу C чтобы получить доступ к Set() метод. Но обратите внимание, что код в классе A разрешено (потому что он может привести тип к C).

Обратите внимание, что класс B сам не должен быть вложенным. Просто C,

Другой вариант этой темы - объявить интерфейс, содержащий только открытые методы, а затем использовать единственную реализацию интерфейса. Опять же, реализующий класс будет вложенным в приватный код, в то время как интерфейс может быть вложенным или нет.

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

В конце концов, вы должны подумать об аудитории кода. Если это небольшая команда, и код полностью полностью самодостаточен для одного проекта, вероятно, достаточно просто сказать всем: "Вы можете подождать с этим, но не гадите с этим событием". В более крупной среде, возможно, несколько команд работают вместе над несколькими смежными проектами, но все еще в основном являются самостоятельным подразделением, используя WaitHandle может быть достаточно. Если вы пишете библиотеку, для которой вы планируете широкое использование, инкапсуляция и закрытые члены и классы - отличный инструмент.:)

Другие вопросы по тегам