2014-10-23 3 views
2

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

private string MyReadOnlyText {get; private set;} 

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

private ManualResetEvent MyEvent {get; private set;} 

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

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

+1

Не могли бы вы просто скомпоновать все примитивные примитивы, которые вы используете в пользовательском классе, который обладает желаемыми свойствами (который может создать только создатель экземпляра/сбросить), а затем использовать его во всем? –

ответ

2

В какой-то степени это кажется чем-то, что может быть рассмотрено конвенцией, т. Е. Просто укажет, что потребитель должен считаться «только для чтения». Простой grep через исходный код должен помочь обеспечить защиту от злоумышленников.

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

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

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

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

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

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

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. Если вы пишете библиотеку, для которой вы используете широкое использование, инкапсуляция и частные члены и классы - отличный инструмент. :)

+0

Отличный ответ, спасибо! – matori82