繁体   English   中英

如何在派生类中隐藏基类公共属性

[英]How can I hide a base class public property in the derived class

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

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

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

您的建议是使用如下所示的代码:

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 派生的某种类型,但它无法分辨是哪种类型——查看代码片段,但更复杂的场景使其几乎不可能)。

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

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

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

但是即使 C# 开发人员确实为此功能付出了巨大的麻烦和代价(记住,功能是昂贵的,尤其是在语言设计中)仍然需要编写防御性代码来避免抛出潜在的 FieldAccessExceptions 的问题,那么有什么优势呢?重组您的继承层次结构您获得了吗? 使用新的成员隐藏功能,将有大量潜在的错误可以潜入您的应用程序和库,从而增加开发和测试时间。

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

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

它应该谨慎,虽然可以使用,只有当像重组继承层次其他的选择会使它复杂得多 这是最后的手段,而不是第一个想到的解决方案。

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

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

public class MyClass : IMyInterface
{

    string IMyInterface.Name { get; set; }

}

您可以 在此处找到更多 信息

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

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

}

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

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

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

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

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

简单的:

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

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

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

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
{

}

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

您需要两个接口,分别用于何时需要该属性和何时不需要该属性,从而使其成为一个可怕的黑客。

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

覆盖声明和覆盖的基方法具有相同的声明可访问性。 换句话说,覆盖声明不能改变虚方法的可访问性。 但是,如果重写的基方法是受保护的内部方法,并且它是在与包含重写方法的程序集不同的程序集中声明的,则必须保护重写方法声明的可访问性。

来自 10.6.4 节覆盖方法

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

如果你想从基类中隐藏一个成员,那么你需要添加一个新的基类,我们称之为 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 { }

你真正需要的是接口:

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

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

对的,这是可能的。 你对代表团怎么说。 我将尝试通过一段代码来了解 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; }
        }
    }
}
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();
        }
    }
}

您可以使用new修改器。

样本;

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.*/ 
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM