【问题标题】:Why would you mask a base class member?为什么要屏蔽基类成员?
【发布时间】:2011-04-06 07:47:05
【问题描述】:

我刚刚学会了如何屏蔽一个基类成员(使用 new),但我错过了我为什么要这样做的重点。掩蔽是否像使用封装一样为我们提供了一定程度的保护?请指教。

【问题讨论】:

  • 不确定我是否理解这个问题,您能否举例说明 you 通过屏蔽基类成员的含义。
  • 虽然派生类不能删除它继承的任何成员,但它可以隐藏它们。 1.要隐藏继承的成员,声明一个相同类型和同名的新成员。 2.还可以隐藏静态成员。躲起来有什么好处?

标签: c# class encapsulation mask


【解决方案1】:

我刚刚学会了如何屏蔽基类成员(使用 new)

仅供参考,此功能通常称为“隐藏”而不是“屏蔽”。我认为“屏蔽”是清除位数组中的位。

我错过了我为什么要这样做的重点。

通常你不想这样做。出于某些使用和不使用此功能的原因,请参阅我 2008 年关于该主题的文章:

http://blogs.msdn.com/b/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx

屏蔽是否像使用封装一样为我们提供一定程度的保护?

不,它没有。

【讨论】:

    【解决方案2】:

    您所指的是Name Hiding。它主要是一个便利功能。如果您从一个不控制其源的类继承,使用new 将允许您更改方法的行为,即使它没有被声明为虚拟(或者如果它是虚拟的,则完全更改签名)。 new 关键字只是禁止编译器警告。您基本上是在通知编译器您有意隐藏父类的方法。

    出于同样的原因,Delphi 使用了 reintroduce 关键字。

    除了隐藏的警告之外,这还能给您带来什么?不是很多。您无法从父类访问 new 方法。如果您的子类直接实现该接口(与从其父类继承它相反),您可以从接口访问它。您仍然可以从孩子那里调用父类的成员。您的类的任何其他后代都将继承 new 成员,而不是父类中的成员。

    【讨论】:

      【解决方案3】:

      我遇到的 only valid safe 示例更具体地使用返回类型或在属性上提供 set 访问器.我并不是说只有这些,但我发现的只有这些。

      例如,假设您有一个非常简单的基础,如下所示:

      public abstract class Base
      {
        public string Name { get; protected set; }
      
        public Base(string name)
        { Name = name; }
      }
      

      你可以有一个看起来更像这样的派生:

      public class Derived : Base
      {
        public new string Name 
        {
          get { return base.Name; }
          set { base.Name = value; }
        }
      
        public Derived(string name) : base(name)
        { }         
      }
      

      假设业务规则允许这个特定的 Derived 具有可更改的名称,我相信这是可以接受的。 new 的问题在于它会根据实例被视为什么类型来改变行为。例如,如果我说:

      Derived d = new Derived("Foo");
      d.Name = "Bar";
      Base b = d;
      b.Name = "Baz"; // <-- No set available.
      

      在这个简单的例子中,我们很好。我们用new 覆盖了这个行为,但不是以破坏性的方式。更改返回类型需要更多技巧。也就是说,如果您使用new 更改派生类型的返回类型,则不应允许基类设置该类型。看看这个例子:

      public class Base
      {
        public Base(Base child)
        { Child = child; }
      
        public Base Child { get; private set; }
      }
      
      public class Derived
      {
       public Derived(Derived child) : base(child)
       {  }
      
       public new Derived Child 
       { get { return (Derived)base.Child; } }
      
      }
      

      如果我可以在 Base 类中使用 set Child,我可能会在 Derived 类中遇到强制转换问题。另一个例子:

      Derived d = new Derived(someDerivedInstance);
      Base b = d;
      var c = b.Child;  // c is of type Base
      var e = d.Child;  // e is of type Derived
      

      我不能通过将我所有的 Derived 类都视为 Base 来破坏任何业务规则,不进行类型检查和强制转换很方便。

      【讨论】:

        【解决方案4】:

        这实际上称为成员隐藏。有几个常见的场景可以适当地使用它。

        • 它允许您解决版本控制问题,其中基类或派生类作者无意中创建了与现有标识符冲突的成员名称。
        • 可用于模拟返回类型的协方差。

        关于第一点……基类的作者以后可能会在派生类中添加与现有成员同名的成员。基类作者可能不了解派生类,因此不期望她应该避免名称冲突。 C# 使用隐藏机制支持类层次结构的独立演化。

        关于第二点......您可能希望一个类实现一个接口,该接口指示某个方法签名,因此您只能返回某个类型的实例,同时您已经对该类型进行子类化并且会真的很喜欢让调用者看到具体的类型。考虑这个例子。

        public interface IFoo { }
        
        public class ConcreteFoo { }
        
        public abstract class Base
        {
          private IFoo m_Foo;
        
          public Base(IFoo x) { m_Foo = x; }
        
          public IFoo Foo { get { return m_Foo; } }
        }
        
        public class Derived
        {
          public Derived(ConcreteFoo x) : base(x) { }
        
          public new ConcreteFoo Foo { get { return (ConcreteFoo)base.Foo; } }
        }
        

        【讨论】:

          【解决方案5】:

          它不仅仅用于掩蔽。它实际上打破了继承链,因此如果调用基类方法,则不会调用派生类中的方法(仅调用基类中的方法)。

          您实际上是在创建一个与基类方法无关的 方法。因此是“新”关键字。

          请记住,如果您想定义与基类型方法具有相同签名但返回类型不同的方法,则可以使用“new”关键字。

          【讨论】:

          • 但这并不能真正解释您为什么要这样做。 (也就是说,你所做的只是告诉你写的函数是如何被调用的——如果它没有被调用,那有什么意义呢?)
          • 为什么?如果您不希望新方法成为具有相同名称和参数类型的虚拟方法的同一继承链的一部分。作为副作用,它允许您重新声明具有相同签名的方法,但返回类型除外。只要你记得调用基类型的方法时它不会被调用。
          • 您的“作为副作用”这句话回答了这个问题。其余的可以通过以下方式更好地完成:a)给方法一个不同的名称,或者 b)根本不写它。
          【解决方案6】:

          您很少会使用“new”来屏蔽基类成员。

          它主要用于派生类先有成员,然后将其添加到基类中的情况——同名不同目的。 new 表示您承认您知道您正在以不同的方式使用它。在 C++ 中添加基成员时,它只是默默地将现有方法合并到继承链中。在 C# 中,您必须在 newoverride 之间进行选择,以表明您知道发生了什么。

          【讨论】:

          • +1 这可能是最常见的用法,但我仍然不喜欢它,因为缺少多态性破坏了派生的意图(谁知道还有什么)。跨度>
          猜你喜欢
          • 2011-06-22
          • 2016-01-19
          • 1970-01-01
          • 2023-04-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-02-14
          • 1970-01-01
          相关资源
          最近更新 更多