【问题标题】:How can I hide a base class public property in the derived class如何在派生类中隐藏基类公共属性
【发布时间】:2009-09-18 11:12:00
【问题描述】:

我想在派生类中隐藏基础公共属性(数据成员):

class Program
{
    static void Main(string[] args)
    {
        b obj = new b();
        obj.item1 = 4;// should show an error but it doent ???
    }
}

class a
{
    public int item1 {get; set;}
    public int item2 { get; set; }
}

class b : a
{
    new private int item1;
}

class c : a
{

}

我将成员设为public,因为我希望在c类中继承该成员,但想在b类中隐藏该成员,我该怎么做?

我没有选择在我的基类中选择性地继承我想要的变量吗???那真的很糟糕,我认为 ms 应该为我们提供一个选项(可能是一个修饰符)来执行此操作


编辑:

我自己找到了答案(我听到很多人说这在 c# 中是不可能的,但你可以做到)

我将代码包括在内,以防它有用

class Program
{
    static void Main(string[] args)
    {
        b obj = new b();
        obj.item1 = 4; // shows an error  : )
    }
}

class a
{
    public int item1 { get; set; }
    public int item2 { get; set; }
}

class b : a
{
    new public static int item1
    {
        get;
        private set;
    }
}

【问题讨论】:

  • 这段代码编译正确吗?
  • 作文。也许甚至值得回答。
  • 这是个坏主意!它打破了OO继承和多态的整个概念。为什么你曾经想要这样做?
  • 对我来说,我这样做是因为我正在创建一个 api。我的 api 对象继承自基类对象,但保护了一些大多数程序员不应该更改的属性和方法。
  • 为什么不直接创建一个包含您要在其中扩展的类的类。然后只需通过访问器公开您想要的任何属性?我认为 Dykam 暗示了这一点。当我想简化一个类时,我会这样做——只显示我关心的属性,并在访问器中有代码来转换基类属性的行为。

标签: c#


【解决方案1】:

我将尝试用例子来解释为什么这是一个坏主意,而不是使用神秘的术语。

您的建议是编写如下所示的代码:

public class Base
{
    public int Item1 { get; set; }
    public int Item2 { get; set; }
}


public class WithHidden : Base
{
    hide Item1; // Assuming some new feature "hide" in C#
}

public class WithoutHidden : Base { }

这将使以下代码无效:

WithHidden a = new WithHidden();
a.Item1 = 10; // Invalid - cannot access property Item1
int i = a.Item1; // Invalid - cannot access property Item1

这正是你想要的。但是,假设我们现在有以下代码:

Base withHidden = new WithHidden();
Base withoutHidden = new WithoutHidden();

SetItem1(withHidden);
SetItem1(withoutHidden);

public void SetItem1(Base base)
{
    base.Item1 = 10;
}

编译器不知道 SetItem1 中的参数 base 将是什么运行时类型,只知道它至少是 Base 类型(或从 Base 派生的某种类型,但它无法判断是哪一个 - 这可能很明显查看代码 sn-p,但更复杂的场景使其几乎不可能)。

因此,在大部分情况下,编译器不会给出 Item1 实际上不可访问的编译器错误。这样就留下了运行时检查的可能性。当您尝试在实际上是 WithHidden 类型的对象上设置 Item1 时,它会引发异常。

现在访问任何成员,任何非密封类(其中大多数)上的任何属性都可能引发异常,因为它实际上是隐藏该成员的派生类。任何暴露任何非密封类型的库在访问 any 成员时都必须编写防御性代码,因为有人可能隐藏了它。

对此的一个潜在解决方案是编写该功能,以便只有声明自己可隐藏的成员才能被隐藏。然后,编译器将禁止对该类型变量的隐藏成员进行任何访问(编译时),并且还包括运行时检查,以便在将其强制转换为基类型并尝试从该类型(运行时)访问时抛出 FieldAccessException .

但即使 C# 开发人员确实为这个特性付出了巨大的麻烦和代价(记住,特性是昂贵,尤其是在语言设计中),仍然必须编写防御性代码来避免这些问题潜在的 FieldAccessExceptions 被抛出,那么与重组继承层次结构相比,您获得了哪些优势?借助新的成员隐藏功能,您的应用程序和库中可能会出现大量错误,从而增加开发和测试时间。

【讨论】:

  • 虽然基类的public 成员通常应该在派生类中可见,但许多类继承了在派生类中不应该可见的protected 成员。例如,我假设 99% 的类将MemberwiseClone 暴露给子派生类,但在大多数情况下(包括List<T>Dictionary<TKey,TValue>,它们都可以从Clone 方法中受益)子派生类不可能使MemberwiseClone 有效地工作,因此应该隐藏该成员。用Obsolete 方法替换可能是最好的,但是...
  • 我知道这个答案很旧,但在您的示例中,SetItem1() 方法应该能够访问 Item1 属性,因为它正在使用 Base 类型。隐藏基础成员并不会完全删除它。它只是在使用该类型或从它派生的类型时使其不可见。我认为这会很有用。当您知道基成员在该上下文中没有意义时,它允许您简化派生类的接口。
【解决方案2】:

您想做的事情直接违背了 OO 的原则,您不能“取消发布”成员,因为这违反了替换原则。您必须将其重构为其他内容。

【讨论】:

  • 所以 Eiffel 弄错了,因为它允许您 undefine 基类的成员?
  • 我不知道 Eiffel 但他们如何处理 a a1 = new b()
  • 啊,我想我错了。你可以取消定义,但显然你需要在类中有一个同名的特性。但是对于多重继承,它的来源不是很清楚(尤其是当存在名称冲突时)。
  • 常见的太恶心了,我想做的就是选择性地继承我的基类中需要的变量,这违反了oo的原则吗?
  • 在 OO 中,派生类是具有附加功能的子集。您应该检查条件:b IS-A a。请使用 PascalCasing 作为类名。
【解决方案3】:

Vadim 的回答让我想起了 MS 如何在某些地方的框架中实现这一点。一般策略是使用EditorBrowsable attribute 隐藏成员对 Intellisense 的影响。 (注意,如果它在另一个程序集中,这只会隐藏它)虽然它不会阻止任何人使用该属性,并且如果他们强制转换为基本类型,他们可以看到它(请参阅我之前的解释)它使它更不容易被发现,因为它不会出现在 Intellisense 中并保持类的界面干净。

不过,它应该谨慎使用,只有当其他选项(如重构继承层次结构)会使它变得很多更复杂时。这是最后的手段,而不是第一个想到的解决方案。

【讨论】:

    【解决方案4】:

    如果您使用接口而不是基类来定义属性,则可以显式实现该属性。这将需要对接口进行显式强制转换才能使用该属性。

    public interface IMyInterface
    {
        string Name { get; set; }
    }
    
    public class MyClass : IMyInterface
    {
    
        string IMyInterface.Name { get; set; }
    
    }
    

    您可以通过here了解更多信息。

    【讨论】:

    • 我发现这是一种将所有内容包含到单个类定义中的好方法,并通过命名消费者可能不应该转换到接口来明确表示,例如类似IMyFactoryInterface
    【解决方案5】:

    我唯一能想到的就是在 a 类中将 item1 设为虚拟:

    class a
    {
        public virtual int item1 { get; set; }
        public int item2 { get; set; }
    
    }
    

    然后在 b 类中重写它,但在 getter 和 setter 中抛出异常。此外,如果在视觉设计器中使用此属性,您可以使用 Browsable attribute 不显示。

    class b : a
    {
        [Browsable(false)]
        public override int item1
        {
            get
            {
                throw new NotSupportedException();
            }
            set
            {
                throw new NotSupportedException();
            }
        }
    }
    

    【讨论】:

    • 即使我这样做了,当我创建一个类 b 的实例时,我也不希望它有一个选项 'item1'
    • 使用 Browsable 属性是他们在 .NET Framework 中的做法。
    【解决方案6】:

    首先,如果您使用一些操作基类的方法,这不是一个好主意。
    您可以尝试使用过时的参数,让用户三思而后行。

    [System.Obsolete("Do not use this property",true)]  
    public override YourType YourProperty { get; set; }
    

    【讨论】:

      【解决方案7】:

      您所描述的内容类似于 C++ 的“私有继承”,在 C# 中不可用。

      【讨论】:

        【解决方案8】:

        您不能直接执行此操作,但您可以覆盖子类中的属性并将它们设为只读,例如

        class Program
        {
            static void Main(string[] args)
            {
                b obj = new b();
                obj.item1 = 4;// should show an error but it doent ???
            }
        }
        
        class a
        {
            public virtual int item1 {get; set;}
            public virtual int item2 { get; set; }
        
        }
        
        class b : a
        {
            public override int item1
            { 
                 get { return base.item1; }
                 set { }
            }
        }
        
            class c : a
        {
        
        }
        

        【讨论】:

          【解决方案9】:

          您可以使用接口来隐藏属性。子类将实现一个没有该属性的接口,然后它就不会出现。

          你需要两个接口来处理你想要的属性和不想要的属性,从而使它成为一个可怕的黑客攻击。

          【讨论】:

            【解决方案10】:

            您可以覆盖它,然后添加一个 [Browsable(false)] 标记以防止它在设计器中显示。

            简单:

            public class a:TextBox
            {
                    [Browsable(false)]
                    public override string Text
                    {
                        get { return ""; }
                        set { }
                    }
            }
            

            【讨论】:

            • 这是在假设 Api 的最终用户正在使用 VS。
            【解决方案11】:

            C# 语言规范明确禁止更改虚拟成员的可访问性是继承类:

            覆盖声明和被覆盖的基方法具有相同的 声明的可访问性。 换句话说,覆盖声明不能 更改虚拟方法的可访问性。 但是,如果 被覆盖的基方法是受保护的内部方法,它在 与包含覆盖方法的程序集不同的程序集 那么覆盖方法声明的可访问性必须受到保护。

            来自第 10.6.4 节覆盖方法

            适用于覆盖方法的相同规则也适用于属性,因此在 C# 中无法通过从基类继承从 publicprivate

            【讨论】:

            • 由于没有澄清这个效果,我想我应该补充一下为什么这个改变不能做的强有力的理由。
            【解决方案12】:

            你真正需要的是接口:

                public interface ProvidesItem1
                {
                    int item1 { get; set; }
                }
            
                public interface ProvidesItem2
                {
                    int item2 { get; set; }
                }
            
                class a : ProvidesItem1, ProvidesItem2
                {
                    public int item1 { get; set; }
                    public int item2 { get; set; }
                }
            
                class b : ProvidesItem1
                {
                    public int item1 { get; set; }
                }
            

            然后只是传递接口。如果这些类应该使用公共实现,请将其放在第三个类中,并让它们从该类派生并实现它们各自的接口。

            【讨论】:

              【解决方案13】:

              是的,这是可能的。你对代表团说什么。我将尝试通过一段代码来了解 OOP 中所谓的“委托”:

              public class ClassA
              {
                  // public
                  public virtual int MyProperty { get; set; }
              
                  // protected
                  protected virtual int MyProperty2 { get; set; }
              }
              
              public class ClassB
              {
                  protected ClassC MyClassC;
              
                  public ClassB()
                  {
                      MyClassC = new ClassC();
                  }
              
                  protected int MyProperty2
                  {
                      get { return MyClassC.MyProperty2; }
                      set { MyClassC.MyProperty2 = value; }
                  }
              
                  protected int MyProperty
                  {
                      get { return MyClassC.MyProperty; }
                      set { MyClassC.MyProperty = value; }
                  }
              
                  protected class ClassC : ClassA
                  {
                      public new int MyProperty2
                      {
                          get { return base.MyProperty2; }
                          set { base.MyProperty2 = value; }
                      }
              
                      public override int MyProperty
                      {
                          get { return base.MyProperty; }
                          set { base.MyProperty = value; }
                      }
                  }
              }
              

              【讨论】:

                【解决方案14】:
                namespace PropertyTest    
                {    
                    class a
                    {    
                        int nVal;
                
                        public virtual int PropVal
                        {
                            get
                            {
                                return nVal;
                            }
                            set
                            {
                                nVal = value;
                            }
                        }
                    }
                
                    class b : a
                    {
                        public new int PropVal
                        {
                            get
                            {
                                return base.PropVal;
                            }
                        }
                    }
                
                    class Program
                    {
                        static void Main(string[] args)
                        {
                            a objA = new a();
                            objA.PropVal = 1;
                
                            Console.WriteLine(objA.PropVal);
                
                            b objB = new b();
                            objB.PropVal = 10; // ERROR! Can't set PropVal using B class obj. 
                            Console.Read();
                        }
                    }
                }
                

                【讨论】:

                • 请写一些简短的文字解释为什么这会解决 OPs 的问题。
                【解决方案15】:

                如果你想从基类中隐藏一个成员,那么你需要添加一个新的基类,我们称之为baseA,你的代码应该如下:

                class Program
                {
                    static void Main(string[] args)
                    {
                        b obj = new b();
                        obj.item1 = 4;// should show an error but it doent ???
                    }
                }
                
                class baseA
                {
                    public int item2 { get; set; }
                }
                class a:baseA
                {
                    public int item1 { get; set; }        
                }
                
                class b : baseA { }
                
                class c : a { }
                

                【讨论】:

                  【解决方案16】:

                  您可以使用new modifer。

                  样品;

                  public class Duck
                  {
                      public string Color{get; set;}
                      public void Swim() { }
                  }
                  public class DonaldDuck : Duck
                  {
                      new public void Swim() 
                      { 
                          /*you could call in DonaldDuck.Swim  only here but not public for DonaldDuck client.*/ 
                      }
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 2013-06-03
                    • 1970-01-01
                    • 2015-04-09
                    • 1970-01-01
                    • 2010-11-23
                    • 2010-11-27
                    • 2015-02-18
                    • 2016-07-03
                    相关资源
                    最近更新 更多