我有以下简单的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
)应如何退出?
请帮助澄清我的困惑,并指出我的一些文件。
答案 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