C# base和this ---转载 小昊
new关键字引起了大家的不少关注,尤其感谢 Anders Liu的补充,让我感觉博客园赋予的交流平台真的无所不在。所以,我们就有必要继续这个话题,把我认为最值得关注的关键字开展下去,本文的重点是访问关键字(Access Keywords):base和this。虽然访问关键字不是很难理解的话题,我们还是有可以深入讨论的地方来理清思路。还是老办法,我的问题先列出来,您是否做好了准备。
• 是否可以在静态方法中使用base和this,为什么?
• base常用于哪些方面?this常用于哪些方面?
• 可以base访问基类的一切成员吗?
• 如果有三层或者更多继承,那么最下级派生类的base指向那一层呢?例如.NET体系中,如果以base访问,则应该是直接父类实例呢,还是最高层类实例呢?
• 以base和this应用于构造函数时,继承类对象实例化的执行顺序如何?
2. 基本概念
base和this在C#中被归于访问关键字,顾名思义,就是用于实现继承机制的访问操作,来满足对对象成员的访问,从而为多态机制提供更加灵活的处理方式。
2.1 base关键字
其用于在派生类中实现对基类公有或者受保护成员的访问,但是只局限在构造函数、实例方法和实例属性访问器中,MSDN中小结的具体功能包括:
• 调用基类上已被其他方法重写的方法。
• 指定创建派生类实例时应调用的基类构造函数。
2.2 this关键字
其用于引用类的当前实例,也包括继承而来的方法,通常可以隐藏this,MSDN中的小结功能主要包括:
• 限定被相似的名称隐藏的成员
• 将对象作为参数传递到其他方法
• 声明索引器
3. 深入浅出
3.1 示例为上
下面以一个小示例来综合的说明,base和this在访问操作中的应用,从而对其有个概要了解,更详细的规则和深入我们接着阐述。本示例没有完全的设计概念,主要用来阐述base和this关键字的使用要点和难点阐述,具体的如下:
using System;
namespace Anytao.net.My_Must_net
{
publicclass Action
{
publicstaticvoid ToRun(Vehicle vehicle)
{
Console.WriteLine("{0} is running.", vehicle.ToString());
}
}
publicclass Vehicle
{
privatestring name;
privateint speed;
privatestring[] array =newstring[10]; public Vehicle()
{
}
//限定被相似的名称隐藏的成员
public Vehicle(string name, int speed)
{
this.name = name;
this.speed = speed;
}
publicvirtualvoid ShowResult()
{
Console.WriteLine("The top speed of {0} is {1}.", name, speed);
}
publicvoid Run()
{
//传递当前实例参数
Action.ToRun(this);
}
//声明索引器,必须为this,这样就可以像数组一样来索引对象
publicstringthis[int param]
{
get{return array[param];}
set{array[param] = value;}
}
}
publicclass Car: Vehicle
{
//派生类和基类通信,以base实现,基类首先被调用
//指定创建派生类实例时应调用的基类构造函数
public Car()
: base("Car", 200)
{ } public Car(string name, int speed)
: this()
{ } publicoverridevoid ShowResult()
{
//调用基类上已被其他方法重写的方法
base.ShowResult();
Console.WriteLine("It's a car's result.");
}
}
publicclass Audi : Car
{
public Audi()
: base("Audi", 300)
{ } public Audi(string name, int speed)
: this()
{
}
publicoverridevoid ShowResult()
{
//由三层继承可以看出,base只能继承其直接基类成员
base.ShowResult();
base.Run();
Console.WriteLine("It's audi's result.");
}
}
publicclass BaseThisTester
{
publicstaticvoid Main(string[] args)
{
Audi audi =new Audi();
audi[1] ="A6";
audi[2] ="A8";
Console.WriteLine(audi[1]);
audi.Run();
audi.ShowResult();
}
}
}
3.2 示例说明
上面的示例基本包括了base和this使用的所有基本功能演示,具体的说明可以从注释中得到解释,下面的说明是对注释的进一步阐述和补充,来说明在应用方面的几个要点:
• base常用于,在派生类对象初始化时和基类进行通信。
• base可以访问基类的公有成员和受保护成员,私有成员是不可访问的。
• this指代类对象本身,用于访问本类的所有常量、字段、属性和方法成员,而且不管访问元素是任何访问级别。因为,this仅仅局限于对象内部,对象外部是无法看到的,这就是this的基本思想。另外,静态成员不是对象的一部分,因此不能在静态方法中引用this。
• 在多层继承中,base可以指向的父类的方法有两种情况:一是有重载存在的情况下,base将指向直接继承的父类成员的方法,例如Audi类中的ShowResult方法中,使用base访问的将是Car.ShowResult()方法,而不能访问Vehicle.ShowResult()方法;而是没有重载存在的情况下,base可以指向任何上级父类的公有或者受保护方法,例如Audi类中,可以使用base访问基类Vehicle.Run()方法。这些我们可以使用ILDasm.exe,从IL代码中得到答案。
.method public hidebysig virtual instance void
ShowResult() cil managed
{
// 代码大小 27 (0x1b)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
//base调用父类成员
IL_0002: call instance void Anytao.net.My_Must_net.Car::ShowResult()
IL_0007: nop
IL_0008: ldarg.0
//base调用父类成员,因为没有实现Car.Run(),所以指向更高级父类
IL_0009: call instance void Anytao.net.My_Must_net.Vehicle::Run()
IL_000e: nop
IL_000f: ldstr "It's audi's result."
IL_0014: call void [mscorlib]System.Console::WriteLine(string)
IL_0019: nop
IL_001a: ret
} // end of method Audi::ShowResult
3.3 深入剖析
如果有三次或者更多继承,那么最下级派生类的base指向那一层呢?例如.NET体系中,如果以base访问,则应该是直接父类实例呢,还是最高层类实例呢?
首先我们有必要了解类创建过程中的实例化顺序,才能进一步了解base机制的详细执行过程。一般来说,实例化过程首先要先实例化其基类,并且依此类推,一直到实例化System.Object为止。因此,类实例化,总是从调用System.Object.Object()开始。因此示例中的类Audi的实例化过程大概可以小结为以下顺序执行,详细可以参考示例代码分析。
• 执行System.Object.Object();
• 执行Vehicle.Vehicle(string name, int speed);
• 执行Car.Car();
• 执行Car.Car(string name, int speed);
• 执行Audi.Audi();
• 执行Audi.Audi(string name, int speed)。
我们在充分了解其实例化顺序的基础上就可以顺利的把握base和this在作用于构造函数时的执行情况,并进一步了解其基本功能细节。
下面更重要的分析则是,以ILDASM.exe工具为基础来分析IL反编译代码,以便更深层次的了解执行在base和this背后的应用实质,只有这样我们才能说对技术有了基本的剖析。
Main方法的执行情况为:
.method public hidebysig staticvoid Main(string[] args) cil managed
{
.entrypoint
// 代码大小 61 (0x3d)
.maxstack 3
.locals init (class Anytao.net.My_Must_net.Audi V_0)
IL_0000: nop
//使用newobj指令创建新的对象,并调用构造函数初始化
IL_0001: newobj instance void Anytao.net.My_Must_net.Audi::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: ldstr "A6"
IL_000e: callvirt instance void Anytao.net.My_Must_net.Vehicle::set_Item(int32,
string)
IL_0013: nop
IL_0014: ldloc.0
IL_0015: ldc.i4.2
IL_0016: ldstr "A8"
IL_001b: callvirt instance void Anytao.net.My_Must_net.Vehicle::set_Item(int32,
string)
IL_0020: nop
IL_0021: ldloc.0
IL_0022: ldc.i4.1
IL_0023: callvirt instance string Anytao.net.My_Must_net.Vehicle::get_Item(int32)
IL_0028: call void [mscorlib]System.Console::WriteLine(string)
IL_002d: nop
IL_002e: ldloc.0
IL_002f: callvirt instance void Anytao.net.My_Must_net.Vehicle::Run()
IL_0034: nop
IL_0035: ldloc.0
//base.ShowResult最终调用的是最高级父类Vehicle的方法,
//而不是直接父类Car.ShowResult()方法,这是应该关注的
IL_0036: callvirt instance void Anytao.net.My_Must_net.Vehicle::ShowResult()
IL_003b: nop
IL_003c: ret
} // end of method BaseThisTester::Main
因此,对重写父类方法,最终指向了最高级父类的方法成员。
4. 通用规则
• 尽量少用或者不用base和this。除了决议子类的名称冲突和在一个构造函数中调用其他的构造函数之外,base和this的使用容易引起不必要的结果。
• 在静态成员中使用base和this都是不允许的。原因是,base和this访问的都是类的实例,也就是对象,而静态成员只能由类来访问,不能由对象来访问。
• base是为了实现多态而设计的。
• 使用this或base关键字只能指定一个构造函数,也就是说不可同时将this和base作用在一个构造函数上。
• 简单的来说,base用于在派生类中访问重写的基类成员;而this用于访问本类的成员,当然也包括继承而来公有和保护成员。
• 除了base,访问基类成员的另外一种方式是:显示的类型转换来实现。只是该方法不能为静态方法。
C# base和this ---转载 小昊的更多相关文章
- 如何理解<base href="<%=basePath%>" ---转载
原文链接http://316325524.blog.163.com/blog/static/6652052320111118111620897/ "base href " 今天在写 ...
- web网站嵌入QQ临时会话代码 ----转载----小技巧
第一种 <img style="CURSOR: pointer" onclick="javascript:window.open('tencent://messag ...
- Git Compare with base,比较大文件时,长时间等待,无法加载
问题 当使用Git比较一个大文件(几十兆数量级)版本见差异时,会一直等待加载,且内存消耗很大,导致其他进程很难执行.任务管理器中,可以看到此时的TortoiseGitMerge吃掉3G左右的内存. 原 ...
- 应用程序框架(一):DDD分层架构:领域实体(基础篇)
一.什么是实体 由标识来区分的对象称为实体. 实体的定义隐藏了几个信息: 两个实体对象,只要它们的标识属性值相等,哪怕标识属性以外的所有属性值都不相等,这两个对象也认为是同一个实体,这意味着两个对象是 ...
- OpenCV3 Ref SVM : cv::ml::SVM Class Reference
OpenCV3 Ref SVM : cv::ml::SVM Class Reference OpenCV2: #include <opencv2/core/core.hpp>#inclu ...
- Python类中super()和__init__()的关系
Python类中super()和__init__()的关系 1.单继承时super()和__init__()实现的功能是类似的 class Base(object): def __init__(sel ...
- 前端编码规范之CSS
"字是门面书是屋",我们不会去手写代码,但是敲出来的代码要好看.有条理,这还必须得有一点约束~ 团队开发中,每个人的编码风格都不尽相同,有时候可能存在很大的差异,为了便于压缩组件对 ...
- 51单片机C语言学习笔记4:keil C51绝对地址访问
在利用keil进行8051单片机编程的时,常常需要进行绝对地址进行访问.特别是对硬件操作,如DA AD 采样 ,LCD 液晶操作,打印操作.等等.C51提供了三种访问绝对地址的方法: 1. 绝对宏: ...
- Fiddler 教程 转自小坦克
-- 此文章是转载小坦克的;直接复制文章的目的是因为原文章地址经常被重置,找不到原来的文章.小坦克博客园主页:https://home.cnblogs.com/u/TankXiao/ 目录 Fiddl ...
随机推荐
- oracle创建新的用户 创建序列 并生成自动自增
1.用有dba权限的用户登录:sys用户 system 口令:manager 2.创建一个新用户:create user abc identified by 123456; 3.授予DBA权限: gr ...
- Billman_ford货币升值——正权回路
2240和1860那个题目很像啊 都是问货币能不能增多,钻社会制度得空子啊哈哈 唯一不同得是你的起点是任意一个点,这个比较麻烦了,多了一层循环嘞 处理货币名可以用map分配id 然后就是老套的Bill ...
- Oracle SQL Trace 和 10046 事件
http://blog.csdn.net/tianlesoftware/article/details/5857023 一. SQL_TRACE 当SQL语句出现性能问题时,我们可以用SQL_TRAC ...
- cxgrid动态显示行号
uses cxLookAndFeelPainters; type TMyCxGrid = class(TObject) class procedure DrawIndicatorCell( ...
- 限制用户不能删除SharePoint列表中的条目(项目)
概述 SharePoint列表提供了一个用于在线协作的电子表格,不同的用户可以同时在不同的地方编辑一套数据. 列表功能在收集用户信息.提供审批流程方面为办公人员提供了非常便捷的好处. 既然是协作办公, ...
- redis的常用公共方法(2)
之前已经写过一篇redis公共方法的使用(https://www.cnblogs.com/jhy55/p/7681626.html),可是发现在高并发的时候出现 Unknown reply on in ...
- 为什么不能用Abort退出线程
在使用线程时,如果线程还未结束直接退出线程很有可能会导致数据丢失. class threadAbort { static void Main(string[] args) { WriteMessage ...
- php类模块引擎PDO操作MySQL数据库简单阐述
PDO是什么呢? 通俗说就是别人写的一个“数据库操作工具类”,它非常强大,可以应对市面上几乎所有主流数据库, 具体应用时候有这样一个关系: 即,要操作某种数据,就得去“打开”对应的pdo引擎. 在ph ...
- Kotlin 初体验
本文由作者邹丽萍授权网易云社区发布. 背景 Kotlin 是 JetBrains 公司(著名的 IntelliJ IDEA 正是由这家公司开发的,Android Studio 也是基于 IDEA 的) ...
- IAP远程在线升级
IAP远程在线升级 在上一篇中实现了LWIP网口通讯,那么肯定要加个在线升级功能,这个功能所占用的资源很少,但在物联网中很重要也很实用.在线升级就是像手机一样,先下载好系统,然后点击升级~然后就没然后 ...