Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如果安全性不是问题,那么不可变接口是一种好模式吗?_C#_.net_Vb.net_Interface_Immutability - Fatal编程技术网

C# 如果安全性不是问题,那么不可变接口是一种好模式吗?

C# 如果安全性不是问题,那么不可变接口是一种好模式吗?,c#,.net,vb.net,interface,immutability,C#,.net,Vb.net,Interface,Immutability,通常建议将不可变的类密封起来,以向使用者保证观察到的类属性将保持不变。当然,对于在安全上下文中使用的类来说,这似乎是一个很好的实践。另一方面,在许多情况下,拥有许多具有公共基本特性的不可变类以及此类类的可编辑版本可能会很有用 例如,图形程序可能有一个包含位置、字体和字符串的DrawnText对象,以及一个派生的DrawnFancyText字符串,该字符串向形状周围的曲线文本添加参数。在某些上下文中,拥有这些对象的不可变版本可能很有用(例如,对于撤消缓冲区之类的东西),但在其他上下文中,拥有可变版

通常建议将不可变的类密封起来,以向使用者保证观察到的类属性将保持不变。当然,对于在安全上下文中使用的类来说,这似乎是一个很好的实践。另一方面,在许多情况下,拥有许多具有公共基本特性的不可变类以及此类类的可编辑版本可能会很有用

例如,图形程序可能有一个包含位置、字体和字符串的DrawnText对象,以及一个派生的DrawnFancyText字符串,该字符串向形状周围的曲线文本添加参数。在某些上下文中,拥有这些对象的不可变版本可能很有用(例如,对于撤消缓冲区之类的东西),但在其他上下文中,拥有可变版本可能更有用

在这样的上下文中,有些上下文需要一个可读的DrawnPancytext对象,但不关心它是否可变,但有些上下文需要DrawnText或DrawnPancytext的不可变派生,但不关心哪个。实现前者需要EditableDrawnPancytext和ImmutableDrawnPancytext有一个共同的基础;实现后者需要ImmutableDrawnText和ImmutableDrawnFancyText有一个共同的基础。不幸的是,没有多重继承就无法实现这种模式,因为ImmutableDrawnText与EditableDrawnFancyText没有关系。幸运的是,接口确实允许多重继承,即使类不允许

实现正确继承关系的最佳方法似乎是定义接口:

  • IDrawnText
  • IDrawnFancyText:IDrawnText
  • IEditableDrawnText:IDrawnText
  • IEditableDrawnPancytext:IEditableDrawnText,IDrawnFancyText
  • IImmutableDrawnText:IDrawnText
  • IIMMutableDrawnPancytext:IImmutableDrawnText,IIDrawnPancytext 让类的使用者使用接口而不是类似乎可以实现所有正确的对象关系。另一方面,公开接口意味着消费者必须相信,没有人用允许外部变异的对象实现所谓的“不可变”接口

    对于非安全敏感信息,使用接口以允许适当的继承关系,并依靠实现者不违反契约,这是否好

    理想情况下,可以充分公开一个公共接口,以允许传递外部实例,而不必允许外部代码定义自己的实现。如果这是可行的,那似乎是最佳方法。不幸的是,虽然可以使用“内部”限定的构造函数公开公共抽象类,但我不知道接口是否具有这种能力。尽管如此,我不确定是否有人用允许外部变异的对象实现“IImmutableDrawnText”一定是一个真正的问题

    编辑
    IDrawnText将只公开getter而不公开setter,但其文档将明确声明实现IDrawnText的对象可能通过其他方式是可变的,也可能不可变的;IImmutableDrawnText将公开与IDrawnText相同的成员,但文档将明确声明禁止允许变异的类实现接口。没有任何东西会阻止可变类违反合同实现IImmutableDrawnText,但任何和所有此类类都将是该接口的中断实现。

    接口不定义可变性/不可变性;相反,它只会限制消费者的行为。仅仅因为一个接口不公开突变设施并不意味着你应该假设对象是不可变的。例如,
    IEnumerable
    没有说明集合的不变性,但它不是一个可变接口

    基本上:您需要在其他地方(可能在文档中)定义它

    如果这种拆分功能的方式对您的域有帮助,那么:开始吧!如果没有帮助,不要不必要地强加给自己(永远不要发明需求)


    另外:如果一个类型不是
    密封的
    ,它就不能真正声称是不可变的,因为(基)类型本身甚至不知道什么是可能的。同样,这可能是也可能不是一个真正的问题。

    没有“不变的接口”这样的东西。有一个接口不声明改变对象的方法,但它没有办法禁止改变。允许变异允许所有线程安全和“安全”问题

    不可变类应该被密封的原因是,它们承诺一旦创建就不能(通常)修改它们。一个子类可能会违背这个承诺(以及LSP),而禁止继承是实现这个承诺的唯一途径

    顺便说一句,不变性不是为了安全。一种允许反射的语言/框架(比如说,C#/.net?)可以用来随意修改对象,忽略对象为阻止它所做的一切。不变性主要是让这一点很难意外实现。

    如果您想实现此模式,可以查看WPF类以获得灵感

    其描述如下:

    定义具有可修改状态和只读(冻结)状态的对象。派生自Freezable的类提供详细的更改通知,可以使其不可变,并且可以克隆自身


    不变性与安全性无关……而与健全接口有关的一切都构成了任何实现它们的类都会做出的承诺,并且任何接口都有可能以违背其隐含承诺的方式实现。可以启动实现IEnumerable.GetEnumerator的集合