C# 继承和访问修饰符

C# 继承和访问修饰符,c#,inheritance,C#,Inheritance,我有以下简单的C代码,但我不理解输出 using System; namespace ConsoleApplication4 { class myParent { public int id = 3; private string name = "Parent class private string"; public void mymethod() { Console.WriteLine(

我有以下简单的C代码,但我不理解输出

using System;

namespace ConsoleApplication4
{
    class myParent
    {
        public int id = 3;
        private string name = "Parent class private string";
        public void mymethod()
        {
            Console.WriteLine("{0} & {1}", name, id);
        }
    }

    class myChild : myParent
    {
        private string name = "Child class private string";
    }

    class Program
    {
        static void Main(string[] args)
        {
            myChild c1 = new myChild();  
            c1.mymethod();
            Console.ReadLine();
        }

        //Output
        //Parent class private string & 3
    }
}
当我调用
c1.mymethod()
时,为什么在
myParent
类中使用
string name
而不是
myChild
类中使用
string name
,因为我调用了
myChild
对象上的方法,该对象具有定义的
string name
变量

我曾经相信,继承意味着简单地将代码从基类复制粘贴到派生类,以重用代码或保存关键字。但经过一些研究,情况似乎并非如此。调用继承的方法会以某种方式引用基类,这可能会解释代码中的输出

然而,我仍然不清楚继承的内在作用。例如,我从未创建基类的实例。基类方法(
myParent.mymethod()
)应该如何退出

请帮我澄清我的困惑,并给我指一些文档

当我调用c1.mymethod()时,为什么要使用myParent类中的string name而不是myChild类中的string name,因为我在调用myChild对象上的方法时,该对象具有已定义的string name变量

方法
c1.mymethod()
仅在
myParent
类中定义。因此,当您调用该方法时,它将使用最接近该方法的
名称
。换句话说,它将首先在
myParent
类中搜索该变量,如果找到,它将使用它

但是,如果您这样做了(将
myMethod
设置为虚拟,并在
myChild
中覆盖它):

然后它将使用
myChild
类中的
name
变量,因为这是最接近的变量

如果您这样做,您将遇到类似的情况:

public class Person
{
    private string name = "John";
    public Person(string name)
    {
        // The parameter is named `name` and the field is named `name` 
        // so the compiler is going to choose the closest variable.
        // In this case, it will assign `name` parameter to itself.
        // Visual Studio is nice, in this case, to give you a warning but 
        // your code will compile and the compiler will just assign `name`
        // to `name`
        name = name;

        // If you did this: this.name = name; 
        // then the private field will be assigned the value of the parameter
    }
}
根据C#规范:

以下是固有性的定义:

继承意味着一个类隐式地包含它的所有成员 直接基类类型,实例构造函数除外, 基类的析构函数和静态构造函数

现在,关于扩展基类

派生类扩展其直接基类。派生类可以添加 将新成员添加到它继承的成员,但它无法删除该定义 继承成员的

换句话说,您可以通过添加新定义(或覆盖现有定义)来扩展基类,但不能删除任何定义

为了让它更干净:

派生类可以通过声明新成员来隐藏(§3.7.1.2)继承的成员 具有相同姓名或签名的成员。但是请注意,隐藏 继承的成员不会删除该成员-它只会使 无法通过派生类直接访问成员

在派生类中执行的操作称为隐藏,从引号中可以看出,它不会删除该成员


因为在您的
MyParent
类中,您使用的是同一类中定义的
name
字段,所以它将始终打印它所做的事情。要更改此行为,您应该查看属性。

如果要访问派生类中的字段,可以将其定义为

并在子构造函数中设置它

class myChild : myParent
{
    public myChild()
    {
        name = "Child class private string";
    }
}
或者,如果将
name
定义为属性{get;set;},则可以使用keyworkds

class myParent
{
    public int id = 3;
    protected virtual string name { get; set; } = "Parent class private string";
    public void mymethod()
    {
        Console.WriteLine("{0} & {1}", name, id);
    }
}

class myChild : myParent
{
    protected override string name { get; set; } = "Child class private string";
}
我从未创建基类的实例

它是在实例化派生类时在内部创建的

当我调用c1.mymethod()时,为什么要使用myParent类中的string name而不是myChild类中的string name,因为我在调用myChild对象上的方法时,该对象具有已定义的string name变量

基本上,您的原始代码在其输出方面与上述情况相当,即在派生类中使用
new
关键字的
virtualname

class myChild : myParent
{
    new string name { get; set; } = "Child class private string";
}

在这种情况下,父级方法将显示父级的
名称
,因为子级的
名称
现在是一个不同的新变量,并且不再是父级方法中使用的变量。

Private是对字段最严格的访问。这意味着没有其他类可以访问它,只有当前类。每个类都有自己的私有字段集

应用程序的行为类似于此的原因是您的
mymethod()
成员已声明为public。这意味着任何类都可以调用它。由于您继承了该方法,因此可以在
myParent
中自动获取该方法。它不是复制到
myparent
,而是继承的。由于它没有被
myChild
覆盖,对
myChild.mymethod()
的调用将在
myParent
上调用它,这将访问它可以访问的唯一私有
name
字段(在
myParent
中)

如果要继承name字段,使其行为更像预期的那样,则需要将该字段设置为受保护的,而不是私有的

class myParent
{
    public int id = 3;
    protected string name = "Parent class private string";
    public void mymethod()
    {
        Console.WriteLine("{0} & {1}", name, id);
    }
}

class myChild : myParent
{
    public myChild()
    {
        name = "Child class private string";
    }
}

现在,
myParent
中的变量在实例化时被
myChild
覆盖。因此,当您调用
myChild.mymethod()
时,它可以访问新值。

您在
myChild
类中的新字段
name
不会使基类中继承的字段
name
消失!它就藏在里面。好的,它已经隐藏了,因为它是私有的,但是在派生类中引入一个新字段仍然不会使隐藏字段消失

如果需要只读的
名称
,可以使用不带
设置
访问器的
受保护的虚拟
属性,并在派生类中重写它:

class myParent
{
    public int id = 3;
    protected virtual string name => "Parent class private string";
    public void mymethod()
    {
        Console.WriteLine("{0} & {1}", name, id);
    }
}

class myChild : myParent
{
    protected override string name => "Child class private string";
}
如果要将其保留为
私有
字段,请提供派生类可以链接的构造函数:

class myParent
{
    public int id = 3;
    private string name;

    public myParent() : this("Parent class private string")
    {
    }
    protected myParent(string name)
    {
        this.name = name;
    }

    public void mymethod()
    {
        Console.WriteLine("{0} & {1}", name, id);
    }
}

class myChild : myParent
{
    public myChild() : base("Child class private string")
    {
    }
}

这是因为您在父级上下文中使用了一个方法。由于您尚未重写myMethod(),因此将使用priva
class myParent
{
    public int id = 3;
    protected virtual string name => "Parent class private string";
    public void mymethod()
    {
        Console.WriteLine("{0} & {1}", name, id);
    }
}

class myChild : myParent
{
    protected override string name => "Child class private string";
}
class myParent
{
    public int id = 3;
    private string name;

    public myParent() : this("Parent class private string")
    {
    }
    protected myParent(string name)
    {
        this.name = name;
    }

    public void mymethod()
    {
        Console.WriteLine("{0} & {1}", name, id);
    }
}

class myChild : myParent
{
    public myChild() : base("Child class private string")
    {
    }
}
namespace ConsoleApplication2
{
class myParent
{
    public int id = 3;
    private string name = "Parent class private string";
    public void mymethod()
    {
        Console.WriteLine("{0} & {1}", name, id);
    }
}

class myChild : myParent
{
    private string name = "Child class private string";
    public new void mymethod()
    {
        Console.WriteLine("{0} & {1}", name, id);
    }
}

class Program
{
    static void Main(string[] args)
    {
        myChild c1 = new myChild();
        c1.mymethod();
        Console.ReadLine();
    }

    //Output
    //Parent class private string & 3
}
}
static void Main(string[] args)
{
    myParent c1 = new myChild();
    c1.mymethod();
    Console.ReadLine();
}