为什么.Net中存在'sealed'关键字?

时间:2009-02-16 22:47:36

标签: .net keyword sealed

.Net框架中的大量类被标记为“已密封”,从而阻止您使用自己的类继承这些类。当然,这违背了面向对象的本质,您可以在其中扩展和重新定义现有对象的行为。

是否有充分理由存在'sealed'关键字?

  

举个例子,   NotifyCollectionChangedEventArgs in   Silverlight是密封的。我想   创建我自己的版本   支持的ObservableCollection   AddRange和RemoveRange,但是   NCCEA的Silverlight版本没有   提供支持的构造函数   NewItems和NewItems的多个项目   OldItems属性,已定义为IList。通常,我只是   使用我自己的变体扩展类   超越NewItems和   OldItems属性,但在这种情况下   我不能,我看不出理由   应该是这种情况。

9 个答案:

答案 0 :(得分:21)

将类(或框架)设计为可扩展并非易事,简单地说继承并不是面向对象编程的单一原则。

因此sealed允许开发人员/设计人员表达并保留这些意图。通过减少维护负担,密封课程也可以使他们的生活更轻松。它允许原始开发人员控制类(或框架)的扩展方式,这样他们就可以进行内部更改,而不必担心会破坏对其他代码的更改。

一个原则是开发人员应默认密封任何 leaf 类。然后,当开发人员故意创建一个未密封的类时,它会强制他们考虑可扩展性。


参考:Eric Lippert - Why Are So Many Of The Framework Classes Sealed?

答案 1 :(得分:6)

我今天提出的一个有点相关问题的回答可能有助于澄清密封课程的目的:

  

我发现自己也是这么想的   问题,直到我开始工作   我自己的可重用库。许多   你结束某些课程的时间   只是不能没有延长   需要模糊或神秘的序列   来自执行者的电话。

     

允许你的课程   延长,你要问:如果一个   开发人员扩展我的课程并通过   这个新课程可以到我的图书馆   透明地使用这个新的   类?我可以正常使用吗?   新课?真的是这个新课吗?   会表现得一样吗?

     

我发现大部分时间都是   .Net Framework中的密封类   有一定的引擎盖   您不了解的要求,   鉴于目前的情况   实施不能安全   暴露给子类。

Liskov Substition and Composition

现在点击该链接并提交实际作者。

答案 2 :(得分:2)

简短的回答是,因为微软这么说。更长的答案是Microsoft提供了一种扩展密封类的机制,称为扩展方法。

通常,扩展您没有源代码的类是个坏主意。例如,您不知道调用基本方法对对象的内部数据的作用。是的,你可以使用反射器或其他任何东西来解决它,但一般来说使用合成或扩展方法要好得多。

你还必须考虑实际上是什么遗产。它不仅仅是一种改变类的方法,它还提供了多态性。如果更改字符串类的语义,然后将新的字符串类传递给期望字符串以特定方式操作的对象,该怎么办? sealed本质上强制执行合同,该对象将始终按照您期望的方式工作。

答案 3 :(得分:2)

如果不深入挖掘,请理解微软有利于在有潜在的安全性,可维护性或向后兼容性问题时对其进行密封,而这些问题将不得不在下游进行。例如,出于安全性和性能原因,System.String已被密封。

在这种特殊情况下,您需要询问Microsoft开发人员为何选择密封该类。然而,我最近一直在阅读的建筑指导文献倾向于采用“密封方法,除非你知道它需要延长。”该文献倾向于在可能的情况下使用扩展方法。 (我不是说我同意它;我只是说这是我最近一直在阅读的内容。)

即使班级没有密封,有问题的房产也可能不是虚拟的,这仍然会让你离开这里的小溪。

在您的特定场景中,我将使用您自己的唯一名称的扩展方法。这就是你所能做的一切。

答案 4 :(得分:1)

虽然我确实认为.NET可能过于密封,但通常是为了保护生态系统的完整性。当您的首要任务之一是保持整体框架/ API /运行时稳定,并且类之间存在一些脆弱的相互依赖性时,最安全的做法是防止人们覆盖该行为并无意中破坏核心功能的稳定性。

尽管如此,我确实倾向于认为.NET团队密封了太多的课程。密封件有时可能是开发人员的简单懒惰,因为适当的类设计会起到太多作用。

答案 5 :(得分:1)

我建议除了保护愚昧人之外,没有充分的理由进行密封课程,我的意思是无辜。你知道那句古老的说法,“给他们足够的绳索,他们会自己上吊”。让他们摇摆我说。也许这是我的C ++背景,但我知道如果我不勤奋的话,我有能力把事情搞得一团糟。

我倾向于按界面编程。接口当然是公共的,任何人都可以自由地提供符合接口所表达的合同的实现。我实现这些接口的具体类往往是一个私人问题,并标记为内部和/或私有。我觉得我不需要密封这些课程。

在需要重用代码的地方,我避免通过继承重用,支持组合和其他技术。

密封对于被认为是普通旧数据类型的类型也可能有效,但我不相信这一点。

答案 6 :(得分:0)

这取决于,有些类只是为了实例化(继承,如果存在,仅用于简化实现),其他类旨在被继承以提供特定的实现。

密封课程有一些优点:

  • 他们没有任何虚拟方法,所以他们不必担心非“异常安全”实现的覆盖方法。
  • 如果一个类是不可变的,它可以保留并保证不变性。

否则,如果您想用“舒适”方法装饰密封类,请使用扩展方法(C#3.0)。

答案 7 :(得分:0)

仅仅因为某事物是一个物体,并不意味着它总是应该为每个人提供扩展或重新定义它的行为。

一个很好的比喻就是门和锁。门的本质是让人们通过它,这就是它的建造。然而,大多数门设计有锁,可以限制人们通过它们的能力。关于门是否有锁以及该锁是否默认锁定的决定留给房间的建筑师,根据房间内的内容和谁应该访问它,而不是它是一扇门。

当然,如果特定建筑物中的大多数门在默认情况下被锁定,这可能会令人沮丧,特别是如果有一个你真正想要通过的那个。 :-)我自己去过那里,我也问了同样的问题。简短的回答是,有时候在设计一个复杂的框架时,人们倾向于在更多的cautios方面犯错,并且默认情况下密封了类,除非有明确的情况需要对它们进行扩展。

答案 8 :(得分:0)

我发现密封关键字有用的一个实际用途是避免“脆弱的类问题”。这是一段视频,展示了一个脆弱的基类问题http://www.youtube.com/watch?v=xnA3RUJcyY4

让我详细解释一下。

考虑下面的场景,我们有一个名为“DbParent”的父类,其虚拟方法为“Insert”。

类DbParent {

    public virtual void Insert()
    {
        Console.WriteLine("Parent insert");
    }

}

下面是一个简单的子类,它有自己的“插入”实现,安装在不同的位置。

  class DbChildClass : DbParent
  {
  public void  Insert()
    {
        Console.WriteLine("Child inserts");
    }

}

现在让我们假设经过几个月的部署后,父类开发人员在不了解对子类的影响的情况下去添加一个新方法“添加”。这个“添加”方法在内部调用“插入”方法(下面是相同的代码片段)。

  class DbParent
  {
  public  void Add() // Adds method with out consulting
  {
  this.Insert();
  }

  public virtual void Insert()
  {
        Console.WriteLine("Parent insert");
  }
  }

现在,通过创建子类对象来调用“Add”方法的客户端程序期望应该调用“Child”类“Insert”实现。

  DbChildClass o = new DbChildClass();
  o.Add();
  Console.Read();

但是whoaaaa,如果你运行下面的代码,你会看到父类“插入”被调用,而不是EXPECTED。

所以我会把这个班级标记为“密封”以避免这些问题。