2009-09-18 92 views
31

我想隐藏基本公共财产(数据成员)在我的派生类:如何隐藏在派生类的基类的公共财产

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 
{ 

} 

我有成员作为公众,因为我想要的成员要在c类中继承,但想隐藏b类中的成员,我该怎么做?

不要我有选择性地继承我想在我的基类中的变量?这就是非常糟糕的,我认为微软应该为我们提供了一个选项(可能是修改)来执行此


编辑:

,我发现自己的答案(我听到很多人讲这是不可能在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; 
    } 
} 
+0

此代码是否正确编译? – vpram86 2009-09-18 11:15:10

+0

我相信如果你告诉我们这个用例,还有一个替代解决方案。你为什么想这样做? – 2009-09-18 11:21:56

+0

你的解决方案显然只隐藏了setter。 虽然它停止立即指派,没有什么可以阻止下列: ((a)中OBJ).item1 = 4; B不能说ITEM1永远不会改变,因为它可能的假设下进行编程。它停止了简单的情况,但并非每个案例。更健壮的解决方案是重构继承层次结构。 – ICR 2009-09-18 13:18:41

回答

9

什么你想直接违背面向对象的问题,你不能'取消'成员,因为这违反了替代原则。你必须将其重构为其他东西。

+0

所以艾菲尔得到这个错误,因为它可以让你''定义'基类的成员? – Joey 2009-09-18 11:21:24

+0

我不知道埃菲尔,但他们如何处理'a1 = new b()'? – 2009-09-18 11:23:06

+0

啊,我想我的错误。你可以定义一些东西,但是显然你需要在类中有一个同名的特征。但是对于多重继承,它不是很清楚它来自哪里(特别是当发生名称冲突时)。 – Joey 2009-09-18 11:25:51

0

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

4

我能想到的唯一的事情就是让在课堂上物品1虚拟:

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(); 
     } 
    } 
} 
+0

即使我这样做,当我创建一个类b的实例,我不希望它有一个选项'item1' – ravikiran 2009-09-18 11:29:42

+1

使用Browsable属性是他们如何在.NET Framework中执行此操作。 – ICR 2009-09-19 12:04:06

0

你不能直接做,但你可以重写子类中的属性并使它们只读,例如,

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 
{ 

} 
0

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

当你想要这个属性,当你不需要时,你需要两个接口,因此使它成为一个可怕的黑客。

22

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

你的建议是有一些代码看起来是这样的:

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论证的基础是什么运行时类型,只知道它是至少型基地(或某些类型的从基地派生,但它无法分辨出 - 看代码片段可能很明显,但更复杂的场景几乎不可能)。

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

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

一个潜在的解决方案是编写该功能,以便只有声明自己可隐藏的成员才能隐藏。然后,编译器将不允许对该类型变量(编译时)的隐藏成员进行访问,并且还包含运行时检查,以便在将其转换为基本类型并试图从该类型(运行时)访问时引发FieldAccessException。 。尽管C#开发者确实遇到了这个特性的巨大麻烦和费用(请记住,特性昂贵的,特别是在语言设计中),仍然必须编写防御性代码以避免潜在的FieldAccessExceptions存在的问题抛出,所以你获得了什么优势而不是重组你的继承层次结构?有了新的成员隐藏功能,就会有大量潜在的漏洞涌入您的应用程序和库中,从而增加开发和测试时间。

+0

尽管基类的'public'成员通常应该在派生类中可见,许多类继承了'protected'成员,这些成员在派生类中不应该是可见的。举例来说,我认为99%的类将“MemberwiseClone”暴露给子派生类,但在大多数情况下(包括'List '和'Dictionary ',这两者都可以从'Clone'中受益)方法),子派生类不可能使'MemberwiseClone'有效地工作,因此应该隐藏成员。用'Obsolete'方法替换也许是最好的,但是... – supercat 2015-04-03 21:30:50

+0

...我几乎从来没有看到它完成。 – supercat 2015-04-03 21:31:07

4

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

它应该谨慎使用,但只有在重构继承层次结构等其他选项会使其更复杂时才会使它成为批次。这是最后的手段,而不是首先想到的解决方案。

1
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(); 
     } 
    } 
} 
+1

请写一些简短的文字,解释为什么这会解决OP的问题。 – Spontifixus 2012-11-27 18:39:50

-1

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

简单:

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

这是让Api的最终用户使用VS的推测。 – garfbradaz 2015-12-28 10:56:14

0

更改虚拟成员的可访问是继承类特别地由C#语言规范禁止:

的重写声明和被覆盖的基体的方法具有相同的 宣布可访问性。 换句话说,重写声明不能 更改虚拟方法的可访问性。然而,如果 重写的基方法是受保护的内部和它被声明在 不同组件比含有重写方法 则重写方法的声明可访问必须被保护的组件。

从部分10.6.4覆盖方法

适用于重写的方法同样的规则也适用于性能,通过从基类继承不能在C#完成从public所以要private

+0

由于没有澄清这个效果,我想我应该加上这个改变无法完成的强有力的理由。 – 2013-05-24 15:22:31

4

如果您使用的接口,而不是用于定义属性的基类,你可以明确地执行的财产。这将需要明确的转换到接口来使用该属性。

public interface IMyInterface 
{ 
    string Name { get; set; } 
} 

public class MyClass : IMyInterface 
{ 

    string IMyInterface.Name { get; set; } 

} 

您可以在此找到更多here

+0

我发现这是将所有内容都包含到单个类定义中的好方法,并且通过命名消费者可能不应该将其转换为接口(例如,像'IMyFactoryInterface' – MechEthan 2018-01-20 19:55:41

1

这一切首先是不是好主意,如果你使用一些方法,其操作的基类。
您可以尝试使用过时的参数让用户两次认为使用此属性。

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

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

类节目 { 静态无效的主要(string [] args) {obj = obj = new b(); obj.item1 = 4; //应该显示一个错误,但它会显示? }}

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

class b : baseA 
{   
} 

class c : a 
{ 
} 
0

你真正需要的是接口:

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; } 
    } 

然后,只需绕过接口。如果这些类应该使用一个通用的实现,请将它放在第三个类中,让它们从该类派生,并实现它们各自的接口。

0

是的,这是可能的。代表团对你说了什么?我将尝试通过一段代码给出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; } 
     } 
    } 
} 
相关问题