继承和访问修饰符

时间:2017-02-05 15:08:39

标签: 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("{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()时,为什么在string name类中使用myParent代替string name类中的myChild,因为我在{{}}上调用方法{1}}具有已定义myChild变量的对象?

我曾经认为继承意味着只需虚拟 从基类复制和粘贴代码到派生类,以重用代码或保存键击。但经过一些研究,似乎并非如此。调用一个继承的方法以某种方式引用基类,这可能解释了我的代码中的输出。

但是,我仍然不清楚继承的内在工作。例如,我从未创建过基类的实例。基类方法(string name)应如何退出?

请帮助澄清我的困惑,并指出我的一些文件。

6 个答案:

答案 0 :(得分:3)

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

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

如果您想继承名称字段,那么它的行为将更像您期望的那样,您需要将字段设为protected而不是私有。

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()时,它可以访问新值。

答案 1 :(得分:1)

  

当我调用c1.mymethod()时,为什么myParent类中的字符串名称而不是myChild类中的字符串名称,因为我在myChild对象上调用了一个具有已定义字符串名称变量的方法?

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

如果你这样做了(让myMethod虚拟并在myChild中覆盖它):

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

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

然后它会使用name类中的myChild变量,因为那是最接近的变量。

如果你这样做,你会遇到类似的情况:

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

答案 2 :(得分:1)

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

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

如果您将name定义为属性{get;},则可以使用virtual/override 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类中的字符串名称而不是myChild类中的字符串名称,因为我在myChild对象上调用了一个具有已定义字符串名称变量的方法?

基本上,您的原始代码在输出方面与上述virtual name的情况相同 - 在派生类中使用new关键字

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

在这种情况下,的方法将显示name,因为孩子的name现在是< em>不同的, new 变量,它不再是父方法中使用的变量。

答案 3 :(得分:1)

这是因为你在父母的上下文中使用了一个方法。由于您没有覆盖myMethod(),因此将使用该父上下文中的私有字段执行它。如果您想在获取子项的上下文时使用方法覆盖,则可以创建子对象。

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

答案 4 :(得分:0)

来自C#规范:

以下是如何定义Inhertiance:

  

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

现在关于扩展基类。

  

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

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

为了让它更清洁:

  

派生类可以通过声明new来隐藏(§3.7.1.2)继承的成员   具有相同名称或签名的成员。但请注意隐藏一个   继承的成员不会删除该成员 - 仅仅是这样做   成员直接通过派生类无法访问。

在派生类中执行的操作称为隐藏,正如您从引用中看到的那样,它不会删除该成员。

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

答案 5 :(得分:0)

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

如果您希望name是只读的,则可以使用没有name访问者的protected virtual属性,并在派生类中覆盖它:

set

如果要将其保留为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"; } 字段,请提供派生类可以链接的构造函数:

private