Какой-то шаблон создания необходим в C#

У меня такой тип:

// incomplete class definition
public class Person
{
    private string name;

    public string Name
    {
        get { return this.name; }
    }
}

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

Этот объект также должен запускать событие каждый раз, когда он обновляется его контроллером / построителем.

Подводя итог, согласно предыдущему каркасу определения типа:

  • Person может быть создан только конкретным контроллером.
  • Этот контроллер может Обновить состояние Person (поле name) в любое время
  • Person должен отправить уведомление остальному миру, когда это произойдет.
  • Все остальные типы должны иметь возможность только атрибутов читатьPerson

Как мне это реализовать? Я говорю здесь о контроллере / построителе, но все остальные решения приветствуются.

Примечание: Я мог бы полагаться на модификатор internal, но в идеале все мои вещи должны быть в одной сборке.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
474
6

Ответы 6

Мне нравится иметь интерфейс только для чтения. Затем строитель / контроллер / что угодно может ссылаться на объект напрямую, но когда вы открываете этот объект снаружи, вы показываете только интерфейс.

Используйте интерфейс IPerson и вложенный класс:

public class Creator
{
    private class Person : IPerson
    {
        public string Name { get; set; }
    }

    public IPerson Create(...) ...


    public void Modify(IPerson person, ...)
    {
        Person dude = person as Person;
        if (dude == null)
            // wasn't created by this class.
        else
            // update the data.
    }
}

Создайте интерфейс IReadOnlyPerson, который предоставляет только методы доступа. Попросите Person реализовать IReadOnlyPerson. Сохраните ссылку на Person в своем контроллере. Дайте другим клиентам только версию только для чтения.

Это защитит от ошибок, но не от мошенничества, как с большинством объектно-ориентированных функций. Клиенты могут выполнять приведение к Person, если они знают (или подозревают), что IReadOnlyPerson реализован Person.

Обновление, за комментарий:

Интерфейс только для чтения может также предоставлять делегат события, как и любой другой объект. Идиома, обычно используемая в C#, не мешает клиентам вмешиваться в список слушателей, но соглашение заключается только в добавлении слушателей, так что этого должно быть достаточно. Внутри любого метода доступа или функции set с побочными эффектами изменения состояния просто вызовите делегат события с защитой для случая null (без слушателей).

Этот ответ может быть хорошим, но он не охватывает вторую часть вопроса: Человеку необходимо отправить уведомление остальному миру, когда это произойдет.

Seb 18.09.2008 11:51

Ознакомьтесь с интерфейсом «System.ComponentModel.INotifyPropertyChanged», чтобы узнать о стандартном методе уведомления заинтересованных сторон об изменении свойства объекта.

Adrian Clark 18.09.2008 21:07

Может что-то в этом роде?

public class Person
{
    public class Editor
    {
        private readonly Person person;

        public Editor(Person p)
        {
            person = p;
        }

        public void SetName(string name)
        {
            person.name = name;
        }

        public static Person Create(string name)
        {
            return new Person(name);
        }
    }

    protected string name;

    public string Name
    {
        get { return this.name; }
    }

    protected Person(string name)
    {
        this.name = name;
    }
}

Person p = Person.Editor.Create("John");
Person.Editor e = new Person.Editor(p);
e.SetName("Jane");

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

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

interface IPerson 
{
    Name { get; set; } 
}

и явно реализовать этот интерфейс:

class Person : IPerson 
{
    Name { get; private set; }
    string IPerson.Name { get { return Name; } set { Name = value; } } 
}

затем выполните явное приведение интерфейсов в вашем построителе для установки свойств. Это по-прежнему не защищает вашу реализацию и не является хорошим решением, хотя каким-то образом подчеркивает ваше намерение.

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

Кажется странным, что, хотя я не могу изменить имя объекта Person, я могу просто взять его контроллер и изменить его там. Это не лучший способ защитить данные вашего объекта.

Но, тем не менее, вот способ сделать это:

    /// <summary>
    /// A controlled person.  Not production worthy code.
    /// </summary>
    public class Person
    {
        private string _name;
        public string Name
        {
            get { return _name; }
            private set
            {
                _name = value;
                OnNameChanged();
            }
        }
        /// <summary>
        /// This person's controller
        /// </summary>
        public PersonController Controller
        {
            get { return _controller ?? (_controller = new PersonController(this)); }
        }
        private PersonController _controller;

        /// <summary>
        /// Fires when <seealso cref = "Name"/> changes.  Go get the new name yourself.
        /// </summary>
        public event EventHandler NameChanged;

        private void OnNameChanged()
        {
            if (NameChanged != null)
                NameChanged(this, EventArgs.Empty);
        }

        /// <summary>
        /// A Person controller.
        /// </summary>
        public class PersonController
        {
            Person _slave;
            public PersonController(Person slave)
            {
                _slave = slave;
            }
            /// <summary>
            /// Sets the name on the controlled person.
            /// </summary>
            /// <param name = "name">The name to set.</param>
            public void SetName(string name) { _slave.Name = name; }
        }
    }

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