C#中的私有成员可以被继承吗?

67

刚刚看到一个教程说:

Class Dog
{
  private string Name;
}
Class SuperDog:Dog
{
 private string Mood;
}

接下来有一个 UML 显示 SuperDog 也将继承 Name。我尝试过,但似乎只有公共成员被继承。除非声明为公共,否则我无法访问 Name。


16
@Tim Goodman:我想您的意思是“如果它们是可访问的”,而不是“如果它们被继承”。 - Dirk Vollmar
派生类继承其基类的成员,无论访问说明符是什么。后者只影响谁可以访问它们。struct A{ private: //protected: //public: int x; }; struct B : A{ private: int x; }; int main(){ std::cout << sizeof(A) << '\n'; // 在我的机器上为4 std::cout << sizeof(B) << '\n'; // 在我的机器上为8 std::cout << '\n'; } - Itachi Uchiwa
14个回答

117
派生类可以访问基类的public、protected、internal和protected internal成员。尽管派生类继承了基类的private成员,但无法访问这些成员。然而,所有这些private成员仍然存在于派生类中,并且可以执行与它们在基类中执行相同的工作。例如,假设一个protected基类方法访问一个private字段。为了使继承的基类方法正常工作,该字段必须存在于派生类中。

来自: http://msdn.microsoft.com/zh-cn/library/ms173149.aspx

因此,在技术上是可以的,但实际上是不行的。

10
我通常将文档作为实际的“合同”,但值得注意的是,.NET反射API似乎不同意这一点。如果您尝试使用derivedType.GetField("f", BindingFlags.Instance | BindingFlags.NonPublic)从派生类中提取私有字段,则无法获得任何返回值,即使技术上应该返回所有继承成员。因此,即使它在技术上必须存在,但在底层中,所有相关部分都将其视为实际上不存在。 - Aaronaught
2
@Aaronaught:看起来只有通过反射自己沿着继承树向上走,才能访问私有成员。请参见https://dev59.com/N3RB5IYBdhLWcg3wN1AQ#686623 - Dirk Vollmar
@0xA3:是的,我知道如何解决那个问题,我只是想指出API似乎没有像文档中描述的那样。就API而言,私有成员不会被继承。 - Aaronaught
@Aaronaught:那应该是一种“设计决策”,在微软以外的地方几乎被所有人称为“漏洞”。不过这是个极好的观点,并且与发布的回答非常相关。 - hemp
乍一看,我本来会说:“不,它们是私有的!”不过这个回答很好,因为经过深思熟虑,通过方法或其他方式给出的私有字段的行为是可以被继承的。这意味着派生类型只是没有访问权限而已。虽然差别很小,但非常棒!=) - Will Marcouiller
继承了,但是没有被访问。真是个糟糕的事情! - codegasm

54
从基类继承到派生类的所有内容。标记为private的成员由于完整性目的不可在派生类中访问,如果需要在派生类中使它们可访问,请将成员标记为protected。

在继承上下文中有各种级别的成员访问权限。

public: 基类的所有公共成员在派生类中和派生类的实例中都可以访问。

protected: 基类的所有受保护成员在派生类中可以访问但不能在派生类的实例中访问。

protected internal: 基类的所有受保护且内部的成员在派生类中和在同一程序集中创建的派生类实例中都可以访问。

internal: 基类的所有内部成员在派生类中和在同一程序集中创建的派生类实例中都可以访问。

private: 基类的所有私有成员在派生类中和在派生类的实例中都不可访问。

private protected: 类型或成员只能在声明它的程序集中访问,由该类或从该类派生的类型中的代码所访问。


1
我认为你对于 protected internal 的措辞有误。protected internal 基类成员可见于所有派生类,并且暴露给与基类相同程序集中的所有类。 - Anthony Pegram

20

SuperDog会继承名称字段。

然而,SuperDog无法访问该字段,因此对SuperDog来说没有实际用途。


2

私有成员在派生类中可以被访问:(如果子类嵌套在基类内)

public class Person
{
    private string message;

    public override string ToString()
    {
        return message;
    }

    public static Person CreateEmployee()
    {
        return new Employee();
    }

    class Employee : Person
    {
        public Employee()
        {
            this.message = "I inherit private members!";
        }
    }
}

这个例子的功劳归功于KodefuGuruMSDN的这个帖子中


1

是的,尽管继承者无法访问该成员。

如果你希望他们能够访问它,请将其声明为受保护的


0

虽然有人说过这句话,但以下是为什么派生类需要私有字段的一个例子:

class Program
{
    static void Main(string[] args)
    {
        var r = new Random();

        foreach(var i in Enumerable.Range(0,100))            
            new Derived(r).Printer();            

        Console.Read();
    }
}

public class Base
{
    private Random r;
    public Base(Random r) { this.r = r; }

    protected void Print()
    {
        Console.WriteLine(r.Next(1, 10000));
    }
}

public class Derived : Base
{
    public Derived(Random r) : base(r) { }
    public void Printer()
    {
        base.Print();
    }
}

0
正如其他人所说,私有成员也会被继承。成员访问是一个不同的主题,但从继承的角度来看并非完全没有关联。重要的是要理解,所有成员都会被继承,无论其访问修饰符如何,因为它会影响子类的大小。请考虑以下代码。
public class Foo
{
  private int a;
  public int b;
}

public class Bar : Foo
{
  private int c;
  public int d;
}

Foo在堆上占用16个字节。其中4个用于同步块(syncblock),4个用于类型信息(方法表),另外每个int变量占用4个字节,总共12个字节。另一方面,Bar将占用24个字节。其中4个用于同步块,4个用于类型信息(方法表),4个用于继承自Foo的每个int域,以及4个用于Bar中的每个int域,总计24个字节。


0

不,它们不是。

protected修饰符可以使字段对派生类可用,但从维护的角度来看,这通常被认为是一个坏主意。你应该使用受保护的属性。


7
当然?我会认为它们被继承了,只是对后代类不可访问。为什么?如果将后代类强制转换为基类,如果基类的私有成员没有被继承...那又会发生什么呢? - marc_s
4
它们是被继承的,只是在派生类中不可见。这个问题似乎来自一个对面向对象编程(OOP)不太了解的人,不要混淆术语,以免让他们感到困惑。 - Niki
Marc,你不能使用强制类型转换来访问私有字段数据。私有字段只能使用显式或隐式的 this 修饰符来访问,这意味着代码必须在类内部运行。此外,如果你将 this 强制转换为另一个类(即使是其自身的超类),你只能访问公共项,而不能访问受保护或私有的项。 - Warren Rumak
2
@Warren:再读一遍问题。他知道他不能访问基类的private成员。他想知道它们是否存在 - Niki
1
它们是吗?这是一个有趣的问题,因为文档说它在那里,但实际上没有技术证据!考虑:class X 有私有成员 Aclass Y 派生自 class Xtypeof(Y).GetField("A", Instance | NonPublic | FlattenHierarchy) 返回 null.... typeof(Y).BaseType.GetField("A", Instance | NonPublic | FlattenHierarchy) 返回 A 的 FieldInfo。所以,再一次强调,从 .NET Framework 的角度来看,这些字段并没有被继承。它们仍然 -完全- 存在于基类中。 - Warren Rumak
1
@Warren:这个“证明”很简单:编写一个带有私有成员的基类。创建一个使用该私有成员的受保护或公共方法。在派生类的实例上调用该方法。尽管没有基类实例存在,但它仍然可以访问私有字段。 - Niki

0

私有成员对于类的后代不可访问。

我不确定所有的访问修饰符,但最基本的只有公共和受保护的成员是可访问的。


除了 private 以外的所有访问修饰符都会被继承。 - Cylon Cat
3
@Cylon,请参考最佳答案。Inherited(继承)并不等于Accessible(可访问)。 - Anthony Pegram

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接