代码之家  ›  专栏  ›  技术社区  ›  Romain Verdier

在C语言中需要某种创造性的模式#

  •  4
  • Romain Verdier  · 技术社区  · 16 年前

    我有以下类型:

    // incomplete class definition
    public class Person
    {
        private string name;
    
        public string Name
        {
            get { return this.name; }
        }
    }
    

    我希望这种类型 创建 更新的 使用某种专用控制器/构建器,但我希望它保持 其他类型的只读 .

    此对象还需要在每次由其控制器/生成器更新时触发事件。

    总结一下,根据前面的类型定义框架:

    • 这个 Person 只能由特定控制器实例化
    • 这个控制器可以 更新 国家 ( name 字段)随时
    • 这个 需要发送 通知 当它发生的时候
    • 所有其他类型应该只能 阅读 属性

    我应该如何实现这一点?我在这里讨论的是控制器/构建器,但欢迎使用其他所有解决方案。

    注: 我可以依靠 internal 修饰符,但理想情况下我的所有东西应该在同一个程序集中。

    6 回复  |  直到 16 年前
        1
  •  5
  •   JasonTrue    16 年前

    创建只公开get访问器的接口ireadonlyperson。让人实现IReadonlyperson。将对person的引用存储在控制器中。只为其他客户端提供只读版本。

    这可以防止错误,但不会像大多数oo特性那样出现欺诈。如果客户机碰巧知道(或怀疑)ireadonlyperson是由person实现的,则可以运行时强制转换为person。

    根据评论更新:

    只读接口也可以像任何其他对象一样公开事件委托。通常在c_中使用的习惯用法并不能防止客户机弄乱侦听器列表,但是约定只是添加侦听器,所以这应该足够了。在任何具有状态更改副作用的set访问器或函数中,只需调用带有空(无侦听器)保护的事件委托。

        2
  •  1
  •   Jason Cohen    16 年前

    我喜欢有一个只读接口。然后builder/controller/whatever可以直接引用该对象,但是当您将该对象公开给外部时,只显示接口。

        3
  •  1
  •   fryguybob    16 年前

    使用接口 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.
        }
    }
    
        4
  •  1
  •   cfeduke    16 年前

    我想 internal 是最简单和最好的方法(当然这涉及到多个程序集)。除了执行一些开销较大的堆栈遍历来确定属性setter中的调用方之外,您还可以尝试:

    interface IPerson 
    {
        Name { get; set; } 
    }
    

    并显式实现此接口:

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

    然后在生成器中执行显式接口转换以设置属性。这仍然不能保护您的实现,也不是一个好的解决方案,尽管它确实在某种程度上强调了您的意图。

    在属性设置器中,必须实现事件通知。亲自解决此问题时,我不会为每个属性创建单独的事件和事件处理程序,而是创建一个propertyChanged事件,并在发生更改时在每个属性中触发它(其中事件参数将包括属性名称、旧值和新值)。

        5
  •  1
  •   Will    16 年前

    奇怪的是,虽然我不能更改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; }
            }
        }
    
        6
  •  0
  •   David Thibault    16 年前

    也许是那样的?

    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方法。