11.c#类的成员初始化顺序
转自http://www.cnblogs.com/siceblue/archive/2009/01/15/1376430.html
C#作为一种纯面向对象的话言,为它编写的整个代码里面到处都离不开对象。一个对象的完整的生命周期是从开始分配空间到初始化,到使用,最后是销毁,使用的资源被回收。要想真正写出面高质量的代码,我们就得对这期间每一个阶段是怎么样一个状态,framework都做了些什么,我们又能够做些什么都要有些了解才行。
一般来说大部分程序员对于一个创建好了的对象怎么使用都是比较清楚的,所以本文也就不想就这一部分做太多的说明,重点就集中开对象的创建和销毁这两个阶段,这也是程序员最容易范错误的阶断。本文首先来讲一讲对象成员的初始化,至于对象的释放和销毁,我想放到另外一篇文章里去讲。虽然本文是以C#2005
为例的,但推而广之,对于其它的基于CLS规范的语言应该也是一样的。
首先我们来看看引用类型的成员初始化过程
我们来看一个例子吧
class Program
{
static void Main(string[] args)
{
DriveB d = new DriveB();
}
}
class BaseA
{
static DisplayClass a = new DisplayClass("基类静态成员初始化");
DisplayClass BaseA_c = new DisplayClass("基类实例变量BaseA_c初始化");
public BaseA()
{
Console.WriteLine("基类构造方法被调用");
}
}
class DriveB : BaseA
{
static DisplayClass DriveB_b = new DisplayClass("继承类静态成员DriveB_b初始化");
//static BaseA DriveB_a = new BaseA();
DisplayClass DriveB_c = new DisplayClass("继承类实例变量DriveB_c初始化");
public DriveB()
{
Console.WriteLine("继承类构造方法被调用");
}
}
class DisplayClass
{
public DisplayClass(string diplayString)
{
Console.WriteLine(diplayString);
Console.WriteLine();
}
}
程序动行的结果是:
继承类静态成员DriveB_b初始化
继承类实例变量DriveB_c初始化
基类静态成员初始化
基类实例变量BaseA_c初始化
基类构造方法被调用
继承类构造方法被调用
得出初始化顺序结论:
1)继承类静态成员变量初始化
2)继承类实例变量初始化
3)基类静态静态成员变量初始化
4)基类实例变量初始化
5)基类构造方法调用
6)继承类构造方法调用。
好像结果和JAVA的有点不一样啊,
有点混乱的感觉,搞不懂M$为什么要让初始化按这样的顺序执行,像JAVA那样严格的从基类到派生类多好呀.上例的运行结果说明,
构造函数这么这个和我们通常思路执行的顺序还是有一定的差别.对于实例成员初始化,基本上就是以下步骤执行:
1 类的对象初始化大体顺序上实例成员赋值到构造函数
2 成员赋值初始化按照由子类到父类的顺序
3 构造函数的初始化按照由父类到子类的顺序
从这里我们有一点需要注意的是,因为成员赋值初始化是从子类到父类的,所以在子类的成员赋值初始化的过程中,不要引用父类定义的成员,因为这个时候父类成员还没有开始初始化.需要说明一点的是C#在创建对象的第一步分配内存完成后会动把所有实例成员变量初始化成变量的默认值,例如整型就是0,引用类型就是null.然后才开始进行成员变量初始化的过程.C#并没有提供类似于C++构造函数中成员特殊的初始化方式:
public constructor(int a)i_a(a){}
估计是因为分配内存和初始化的严格分离,以及反射创建对象的需要,而且也不像C++那样追求的是extreme效率的原因吧;而且就像是以前看到有人说过,再好的语法级别的优化都不能改变写得烂的代码带来的效率低下.
我们知道,C#里面的静态成员初始化不同于C++的静态成员初始化.C#里的静态成员只会在必要的时候,确切的说是在第一次访问该类的时候才会进行静态成员的初始化.这样做也是有一定道理的,一是减少了内存的开销,再就是加快了程序集启动的时间,很难想像多一个比较费时的静态初始化在程序启动的时候就一一进行,那样的等待会是比较痛苦的.而且大部分时间我们都只是使用一个程序集里面很少的一部分类,如果把程序集里面所有的类不管三七二十一都预先进行初始化的话,对内存和时间的浪废还是比较大的.
了解了静态成员初始化的时机,就引出了另外一个问题,如果两个类相互间引用,比如A类的静态初始化里引用到了B类,B类的静态
初始化里又引用到了A类,这个时候又会出现什么样的结果呢,还是用例子还说明吧,请看下面这段代码:
using System;
class A
{
public static int X;
static A(){
X=B.Y+1;
}
}
class B
{
public static int Y=A.X+1;
static B(){}
static void Main(){
Console.WriteLine("X={0},Y={1}",A.X,B.Y);
}
}
产生的输出结果是什么?
一般来说静态声明赋值语句先于静态构造函数执行,没有赋值的类成员声明会被初始化成该类型的默认值,也就是说
public static int X;
public static int Y=A.X+1;
比各自所在的静态构造函数先执行,前一句X没有赋值,默认就是0,后一句的Y在没有赋值之前也是0,赋值后就是A.X+1的值。
类的静态初始化包括成员变量的声明赋值,静态构造函数的执行。
静态初始化只有在类第一次被访问的时候才执行,而且是优先于第一次访问该类的代码执行
因为Main函数在class B中,所以程序先执行的是上面的第二条语句,声明一个Y,再给Y赋值
在赋值的时候又用到了A类中的X静态,当第一次访问A.X的时候,会先调用A类的静态构造函数,这里执行赋值X=B.Y+1,而重新去访问B类的成员,因为前面说的静态初始化只有第一次被访问的时候会执行,所以再次访问B类的时候不会重复进行静态初始化的。这时会因为前一次初始化还未完成,特别是B.Y还没有赋值完成,所以根据上面说的,B.Y现在处理只是声明完成的状态,所以现在B.Y的值就是0,相应的得到的X的值就是1了,在A类的静态构造函数执行完成的时候,程序会再回到B中Y的赋值语句上来,这时候得到的A.X的值就是1,而Y赋值完成后,此时值就变成了2了
因此最终输出的结果就是X=1,Y=2
对于引用类型成员的初始化说了这么多还是总结一下吧.C#中初始化变量(包括实例成员变量和静态成员变量)可以采用成员声明的地方赋值的方式,也可以采用构造函数的方式.我个人在使用实例对象的时候比较推荐采用构造函数的方式,因为构造函数赋值的方式执行的顺序是从父类到子类,这种顺序避免了子类成员变量的初始化过程引用了未赋值的父类成员变量.而且在构造函数中初始化变量可以采用更多的语句块,更多的判断逻辑来初始化,甚至可以加上结构化异常处理try{}catch{}来处理异常信息,远比单单一个赋值语句来得灵活.不过对于简单的内置基本类型(如int,Enum,string等)就无所谓在哪里进行初始化了.
以上是引用类型的初始化过程,值类型(这里主要是指的结构类型)的静态初始化和引用类型的完全一致.C#的结构类型是有构造函数的(记得C++里面结构也貌似可以声明构造函数),而实例成员的初始化因为结构没有派生的功能,所以在这方面反而比较简单.但是因为值类型始终是不能为空的,一旦声明就必须要分配相应的内存空间,有了内存空间当然是要首先进行初始化的了,这都是为了保证值类型的有效性吧.这个过程是由Framework来完成的,我们自己是没有办法写代码来控制.因此Framework自己在初始化调用构造函数的时候当然就需要对自己要调用的构造函数的参数作个统一的约定,最简单的就是无参构造函数了.所以在C#的每个结构里都默认隐含了一个无参的构造函数,程序员自己可以重载构造函数,但是不能声明自己的无参构造函数(这个是被Framework占用了的).
有很多刚从C++转到C#的程序员在使用引用类型作为函数的临时变量的时候还能认识到在使用之前需要new一下创建实例再使用,但是在使用结构作为函数的临时变量的时候就喜欢声明后直接拿来使用,问起他们的时候总是说结构是值类型,值类型是存在栈上的,声明后就直接可以使用了.先不论这句话是不是正确的(关于C#中值类型和引用类型到底存在什么地方有时间以后一定写一篇文章专门讨论一下).首先按C#编程规范值类型同样是需要进行成员变量的封装的,很多值类型在声明后就不能够改变,而只声明一个结构体不赋值的话相当于是调用的默认的构造函数,而通常这个默认的构造函数对于我们来说是没有什么意义的.所以得到的值也是没有太大的用处,除非你是想用作out参数所实参,真正用到的时候还得另外赋值.所以当你这样使用结构体的时候,C#编译器会警告你,这个变量只是声明了没有赋值(其实是相当于有一个值,但是没有意义).其实变量使用之前赋值这也是一个很好的习惯,C++里面虽然直接声明了就可以用,但是一般也会在使用之前先ZeroMemory一下,这其实也是相当于初始化了结构体吧,唯一的区别是不需要重新分配空间.
11.c#类的成员初始化顺序的更多相关文章
- Java类的成员初始化顺序
Java类的成员初始化顺序 2017-06-01 代码: public class InitializeSequence { public static void main(String[] args ...
- java类的成员初始化顺序和初始化块知识
java类的成员初始化顺序和初始化块知识 转自:http://blog.csdn.net/lgfeng218/article/details/7606735 属性.方法.构造方法和自由块都是类中的成员 ...
- C#类的成员初始化顺序
首先我们来看看引用类型的成员初始化过程 我们来看一个例子吧 class Program { static void Main(string[] args) { Driv ...
- Java 类的成员初始化顺序
做个简单笔录,就当是重温下基础知识. 1.先看代码: package com.test; public class Test { public static void main(String[] ar ...
- Java类成员初始化顺序
类中包含7中成员:1.静态变量 static2.final静态常量 final static3.静态代码块 static{} //多个代码块顺序执行 4.普通变量5.普通代码块 {} //多个代码 ...
- C++成员初始化顺序
#include <iostream> using namespace std; int seti() {cout << "seti" << e ...
- C#中成员初始化顺序
http://blog.csdn.net/huangcailian/article/details/25958967 一.成员初始化整体顺序 1.成员赋值初始化先于构造函数: 2.成员赋值初始先从子类 ...
- Java中的成员初始化顺序和内存分配过程
Java中的成员初始化顺序和内存分配过程 原帖是这样描述的: http://java.dzone.com/articles/java-object-initialization?utm_source= ...
- java中的多构造函数以及类字段的初始化顺序
1.同一个类可以有多个构造函数,多个构造函数之间通过参数来区分.这是方法重载的一个实例.构造函数之间可以相互调用. 2.类的初始化块:可以在类中使用“{”和“}”将语句包围起来,直接将其作为类的成员. ...
随机推荐
- 【温暖】文龙回AICODER给老马送锦旗了
又是一个愉快的周末,AICODER第一批老学员文龙小伙伴.已经工作两个月,而且就业薪资12000+,文龙从之前月薪不足4000,一下子翻了三倍多的工资. 几个月的实习,让文龙掌握了大前端全栈的技术,在 ...
- JavaWeb过滤器.监听器.拦截器-原理&区别(转)
1.拦截器是基于java的反射机制的,而过滤器是基于函数回调 2.过滤器依赖与servlet容器,而拦截器不依赖与servlet容器 3.拦截器只能对action请求起作用,而过滤器则可以对几乎所有的 ...
- C语言 · 组合数
组合数 从4个人中选2个人参加活动,一共有6种选法. 从n个人中选m个人参加活动,一共有多少种选法?下面的函数实现了这个功能. 请仔细分析代码,填写缺少的部分(下划线部分). 注意:请把填空的答案(仅 ...
- EL表达式具体解释
在 JSP 页面中,使用标签库取代传统的 Java 片段语言来实现页面的显示逻辑已经不是新技术了,然而.由自己定义标签非常easy造成反复定义和非标准的实现.鉴于此.出现了 JSTL ( JSP St ...
- 阿里java代码检测工具p3c
阿里在杭州云栖大会上,正式发布众所期待的<阿里巴巴Java开发规约>扫描插件! 该插件由阿里巴巴P3C项目组研发.这个项目组是阿里巴巴开发爱好者自发组织形成的虚拟项目组,把<阿里巴巴 ...
- Vue:生命周期
一.什么是vue的生命周期 Vue中的生命周期是指组件从创建到销毁的一系列过程.看下面这张官方文档的图: 从图片中可以看出Vue的整个生命周期包括8个状态,按照先后顺序分别为: beforeCreat ...
- java.exe和javaw.exe的区别
相同点:二者都是Java的虚拟机,用来执行Java程序 区别: 1. javaw.exe运行程序时不会输出控制台信息 (注:“w”就是window的意思). 使用案例 start.bat(y以下代码 ...
- VS IISExpress REST DELETE 405 Method Not Allowed
[参考].net IIS MVC Rest api 跨域 PUT DELETE 404 无法使用问题解决方案 今日在使用泛型處理常式處理檔案上傳時,使用了 HTTP 動詞的 PUT.DELETE 進行 ...
- react项目使用bootstrap
曾经对于react项目怎么使用bootstrap纠结了很久,网上也查了好多的资料,有的用react-bootstrap,只要npm install 以后,import就可以使用里面的css了.但是这个 ...
- Jquery EasyUI Combotree只能选择叶子节点且叶子节点有多选框
Jquery EasyUI Combotree只能选择叶子节点且叶子节点有多选框 Jquery EasyUI Combotree单选框,Jquery EasyUI Combotree只能选择叶子节点 ...