我有以下类型:
// incomplete class definition
public class Person
{
private string name;
public string Name
{
get { return this.name; }
}
}
我想要使用某种专用控制器/构建器创建和更新这个类型,但我希望它对其他类型保持只读
此对象还需要在每次由控制器/构建器更新时触发事件。
总结一下,根据以前的类型定义骨架:
Person
只能由特定控制器实例化Person
(name
字段)的状态Person
需要向世界其他地方发送通知 Person
属性我该如何实现?我在这里谈论的是控制器/构建器,但欢迎所有其他解决方案。
注意:我可以依赖internal
修饰符,但理想情况下我的所有内容都应该在同一个程序集中。
答案 0 :(得分:5)
创建一个IReadOnlyPerson接口,它只公开get访问器。让人实施IReadOnlyPerson。将对Person的引用存储在控制器中。仅为其他客户端提供只读版本。
与大多数OO功能一样,这可以防止错误,但不能防止欺诈。客户端可以运行时转发给Person,如果他们碰巧知道(或怀疑)IReadOnlyPerson是由Person实现的。
根据评论更新:
Read Only接口也可以公开事件委托,就像任何其他对象一样。通常在C#中使用的习惯用法不会阻止客户端搞乱侦听器列表,但约定只是添加侦听器,因此这应该是足够的。在具有状态改变副作用的任何set访问器或函数内部,只需使用null(无侦听器)情况的保护调用事件委托。
答案 1 :(得分:1)
我喜欢有一个只读界面。然后构建器/控制器/可以直接引用对象,但是当您将此对象暴露给外部时,只显示接口。
答案 2 :(得分:1)
使用接口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.
}
}
答案 3 :(得分:1)
我认为internal
是最不复杂和最好的方法(当然这涉及多个程序集)。如果没有做一些开销密集的堆栈步骤以确定属性设置器中的调用者,您可以尝试:
interface IPerson
{
Name { get; set; }
}
并明确实现此接口:
class Person : IPerson
{
Name { get; private set; }
string IPerson.Name { get { return Name; } set { Name = value; } }
}
然后在构建器中执行显式接口强制转换以设置属性。这仍然不能保护您的实施,并不是一个好的解决方案,虽然它确实在某种程度上强调了您的意图。
在您的属性设置器中,您必须实现事件通知。自己解决这个问题我不会为每个属性创建单独的事件和事件处理程序,而是创建一个PropertyChanged事件并在发生更改时在每个属性中触发它(其中事件参数将包括属性名称,旧值和新值)。
答案 4 :(得分:1)
奇怪的是,虽然我无法更改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; }
}
}
答案 5 :(得分:0)
也许是这样的?
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方法。