代码之家  ›  专栏  ›  技术社区  ›  X-Cubed

为什么.NET中存在“sealed”关键字?

  •  12
  • X-Cubed  · 技术社区  · 16 年前

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

    “sealed”关键字的存在有充分的理由吗?

    作为一个例子, 中的notifyCollectionChangedEventArgs Silverlight已密封。我想 创建我自己的版本 支持的ObservableCollection 添加范围和删除范围,但是 NCCEA的Silverlight版本没有 提供一个支持 新项目的多个项目和 已定义为IList的olditems属性。通常,我只是 用我自己的变体扩展类 超过了新项目 olditems属性,但在本例中 我不能也看不出为什么 应该是这样的。

    9 回复  |  直到 11 年前
        1
  •  21
  •   Robert Paulson    11 年前

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

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

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


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

        2
  •  6
  •   Community CDub    7 年前

    我今天问的一个相关问题的答案可能有助于澄清封课的目的:

    我发现自己也在问这个问题 直到我开始研究 我自己的可重用库。许多 你上某些课的时候 如果没有 需要模糊或神秘的序列 来自实现者的调用。

    当你允许你的班级 扩展,你必须问:如果 开发人员扩展了我的类,并通过了 这是去图书馆的新课,我可以吗 透明地使用这个新的 班级?我能正常使用这个吗 新班级?这是新课吗 会有同样的表现吗?

    我发现大多数时候 .NET框架中的密封类 一定要在引擎盖下面 你不知道的要求, 考虑到电流 实现不安全 暴露于子类。

    Liskov Substition and Composition

    现在按照这个链接,向上投票给实际的作者。

        3
  •  2
  •   Erik Funkenbusch    16 年前

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

    一般来说,扩展没有源代码的类是一个坏主意。例如,您不知道调用基方法对对象的内部数据做了什么。是的,您可以使用Reflector或其他方法来解决问题,但一般来说,使用组合或扩展方法要好得多。

    你还必须考虑什么是继承。它不仅是一种改变类的方法,还提供了多态性。如果更改了字符串类的语义,那么将新的字符串类传递给希望字符串以特定方式工作的对象,会怎么样?sealed基本上强制执行这个对象将始终按您期望的方式工作的契约。

        4
  •  2
  •   John Rudy    16 年前

    不要太深入地挖掘,请理解,当存在潜在的安全性、可维护性或向后兼容性问题时,微软倾向于密封类,而这些问题将不得不在下游处理。例如, System.String 因安全和性能原因而密封。

    在这个特殊的例子中,您需要问一个微软开发人员为什么选择密封这个类。然而,我最近读到的建筑指导文献倾向于采用“除非你 知道 这本文献倾向于支持在可能的情况下使用扩展方法。(我不是说我同意,我只是说我最近一直在读这个。)

    即使这个类没有被密封,所讨论的属性也可能不是虚拟的,这仍然会把你留在这里。

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

        5
  •  1
  •   Rex M    16 年前

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

    尽管如此,我还是觉得.NET团队封闭了太多的类。密封有时可能是开发人员的简单懒惰,因为一个适当的类设计将是太多的工作。

        6
  •  1
  •   Daniel Paull    16 年前

    我建议,除了保护无知的人之外,没有什么好的理由封杀阶级——呃,我是说无辜。你知道一句老话,“给他们足够的绳子,他们就会自己上吊。”我说,让他们摇摆吧。也许这是我的C++背景,但我很舒服,知道我有能力完全把事情,如果我不勤奋。

    我倾向于按接口编程。接口当然是公共的,任何人都可以自由地提供自己的实现,以遵守接口所表示的契约。实现这些接口的具体类往往是一个私有的关注点,并被标记为内部和/或私有的。我觉得我不需要封印这些课程。

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

    密封在被认为是普通老数据类型的类型上也可能有效,但我不相信威瑟尔会这样做。

        7
  •  0
  •   Michael Damatov    16 年前

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

    密封类有一些优点:

    • 它们没有任何虚拟方法,因此不必担心非“异常-安全”实现的重写方法。
    • 如果一个类是不可变的,它可以保存和保证不可变。

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

        8
  •  0
  •   Franci Penov    16 年前

    仅仅因为某个事物是一个对象,并不意味着它总是应该对每个人都敞开心扉来扩展或重新定义它的行为。

    一个很好的比喻就是门和锁。门的本质是让人们通过它,这就是它建造的目的。然而,大多数门都设计有锁,限制人们通过它们的能力。决定一扇门是否有锁,以及该锁是否默认地被锁上,都是由房间的设计师根据房间里的东西和谁应该访问它来决定的,而不是因为它是一扇门。

    当然,如果特定建筑中的大多数门在默认情况下都是锁着的,这可能会令人沮丧,特别是如果有一个门你真的想穿过的话。:-)我也去过那里,我也问过同样的问题。简而言之,在设计一个复杂的框架时,人们往往会更加谨慎地犯一点错误,并且默认情况下会将类密封,除非有明确的场景需要扩展它们。

        9
  •  0
  •   Shivprasad Koirala    11 年前

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

    让我详细解释一下。

    考虑下面的场景,其中我们有一个名为_156;DBParent_157;的父类,它具有一个虚拟方法_156;INSERT_157;。

    类双亲 {

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

    }

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

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

    }

    现在让_s说,在部署了几个月的父类开发人员之后,在不了解对子类的影响的情况下,去添加一个新方法__add_。此_156;add_157;方法在内部调用__insert_157;方法(下面是相同方法的代码段)。

      class DbParent
      {
      public  void Add() // Adds method with out consulting
      {
      this.Insert();
      }
    
      public virtual void Insert()
      {
            Console.WriteLine("Parent insert");
      }
      }
    

    现在,通过创建子类对象来调用__add_157;方法的客户机程序需要调用__child_ class__insert_157;实现。

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

    但是whoaaa,如果运行下面的代码,您将看到调用父类insert_157;,这是不期望的。

    所以我会去把班级标记为“密封”,以避免出现这样的问题。