实例构造与引用类型

之前的章节其实已经写过了引用类型的构造过程:

首先当然是,在堆中,为引用类型的实例对象分配内存,然后初始化对象的附加字段(即类型对象指针和同步块索引)。

这个时候为对象分配的内存都是直接被置为0的,所以如果所用到的构造器中没有对对象中的一些字段做处理,那么这些字段的初始值都应该为0或者null。

如果一个类,没有构造函数,那么这个类构造的时候就会定义一个默认无参构造器,它里面就简单调用基类的无参构造器。

极少数的情况下,会有不实用实例构造器就能创建类型的实例的情况,比如MemberwiseClone方法(深复制)和反序列化对象时。(反序列化会调用GetUninitializedObject或GetSafeUninitializedObject方法为对象分配内存,还是后面讲吧。)

还记得上一章节写的,内联初始化可能会导致性能问题吧:

因为每一次内联初始化实际上都会将这些初始化字段的操作,嵌入构造函数的代码中(注意会先进行这些操作,再进行真正的构造函数的操作)。如果只有一个构造器函数,那么不会有什么影响。然而如果有多个构造器函数,那么这几个构造器函数里都会插入这段初始化字段的代码。

所以当存在多个构造器参数,而代码里又有一大堆内联初始化,那么实际生成的代码中就会有大量的冗余代码。可以用以下方法解决:

  public class Troy{
//不进行内联初始化
int _a;
int _b;
public Troy() {
//将初始化过程放在某一构造参数内,一般就是无参构造函数中
this._a = ;
this._b = ;
}
public Troy(int i):this() //在其它构造函数时,调用this()
{ }
public Troy(int i,int j) : this()
{ }
}

实例构造与值类型

值类型其实不需要定义构造器,C#编译器也根本就不会为值类型内联嵌入默认的无参构造器。

只有嵌套在引用类型的值类型才会被初始化为0或null,如果是基于栈的值类型,那么在读取之前,被要求强制初始化,否则报错。

值类型的实例构造器只有显式调用才有用,否则其字段都会被初始化为0或null。(也就是说,即时这个struct有无参构造函数,只要你没有显示调用,那么就不会自动调用无参构造函数。实际上C#编译器也不允许你在结构体里写一个无参构造函数,毕竟这个点来说,太容易误会了)。

由于C#不允许值类型定义无参构造函数,所以值类型同样不准内联参数化。(静态字段可以内联初始化,因为是在类型对象里面,而不是实例对象)

且值类型的任何构造函数在初始化的时候,必须对值类型的所有字段都赋值。

对于这么麻烦的设定,当然也有解决的办法:

    public struct Troy {
public int a;
public int b;
public Troy(int i) {
this = new Troy();//先初始化所有的字段都为0或null
//初始化自己想玩的字段
a = i;
}
}

(我不得不吐槽,我刚刚用VS自己写了个值参数初始化的小例子,然后被360给当做病毒删掉了。)

关于类型构造器

首先要了解到,类型构造器实际上就是构造CLR分配内存中的类型对象初始化时用的。

类型构造器是不允许有参数的,当然也就只能定义一个类型构造函数。

实际上类型构造器必须是私有的,甚至不允许显式写上private修饰符,这样做正是为了防止开发人员调用。它的调用总是由CLR负责的。

简单样例:

class Program
{
static void Main(string[] args)
{
Troy obj = new Troy();
}
}
public class Troy {
static Troy() {
Console.WriteLine("我就问你6不6?");
}
}

构造过程:

JIT编译器编译一个方法时,会查看代码引用了哪些类型。任何一个类型定义了类型构造器,那么JIT编译器就会检查针对当前AppDomain是否执行了这个类型构造器。是就不调用,否就调用。因为CLR希望在每个AppDomain中一个类型构造器只执行一次,所以为了不使多个线程同时调用类型构造器,在第一个调用类型构造器的线程调用时,会获取一个互斥线程同步锁。这样一来,就只有一个线程可以调用了,后面的线程要用的时候发现已经调用过了,就不会再调用类型构造器了。(因为类型构造器线程安全,所以很适合在里面初始化任何单例对象。)

虽然可以在值类型中定义类型构造函数,然而实际上因为值类型根本就不会在堆中有类型对象,所以自然里面的代码都不会被调用。

关于操作符重载

实际上CLR对操作符重载一无所知,因为这是编程语言的语法。

当C#这种语言写的操作符重载语句被编译成IL代码时,其实已经变成了一个带有specialname标志的函数。

当编译器看到有+这种操作符时,就会看几个操作数的类型中是否有定义了名为op_Addition这个函数(被编译后的真正的函数名),而且该方法参数兼容于操作数的类型。

所以操作符重载函数中,一定要有一个参数的类型与定于这个重载方法的类型相同:

  public class Troy {
public static int operator +(Troy a, Troy b) {
return ;
}
}

关于转换操作符方法

  class Program
{
static void Main(string[] args)
{
Troy obj = ;//隐式转换成功
string a = obj;//由于是显示转换重载,所以这种写法会编译不过
string a =(String)obj;//显示转换成功
}
}
public class Troy {
// 隐式转换操作符implicit重载
public static implicit operator Troy(Int32 num) {
return new Troy();
}
// 显式转换操作符explicit重载
public static explicit operator String(Troy troy)
{
return "怎么转都是我";
}
}

和一般的+-这种操作符重载一样,实际上生成的IL代码中,换了一个名字,前缀加上了op_。

当C#编译器检测到代码中一个对象期望得到另一个类型不同的对象时,就回去找这两个类型中是否定义了隐式转换的op_Implicit方法,是就转。显示类似。

可以参考Decimal类的定义去理解。

关于扩展方法

先说一下我自己的认识吧,其实我不建议使用这个东西。

因为写得不规范的扩展方法,会增加了代码的阅读难度,增加维护成本。(我真的很确定有的人会把这个东西写得到处都是)

这个东西之前在写《重构》的学习笔记中提到过,主要用于解决别人封装的类库,没法增加自己想要的函数。

第19点.不完美的类库讲到的

简单来讲,还是慎用,自己写的类就别用扩展方法。

另外扩展方法必须是顶级静态类中定义的静态方法,如果是嵌套类中的话,编译会出错。

实际上扩展方法在C#编译器编译过后也只是个一般的静态对象里的静态函数,只不过加了个[Extension]的特性。然而实际上这个ExtensionAttribute特性还不能在代码中用,都是C#编译器去自动生成的。

关于分部方法

分部方法和分部类很像,不过是方法前面加上partial修饰符。

这样的话,如果其它分部类实现了这个方法,那么就会加上这个方法,如果没有实现,那么这条代码在编译的时候就会被忽略。

但是分部方法只能在分部类和结构中用,且返回类型总是void,任何参数都不能用out来修饰。之所以会这样限制,是因为方法在运行时可能就并不存在,所以也就不会有返回。

分部方法总是private的,但是C#编译器禁止将private修饰符显式写在分部方法前面。(和类型构造器在这个点上类似)

【C#进阶系列】08 方法的更多相关文章

  1. 【 D3.js 进阶系列 — 1.2 】 读取 CSV 文件时乱码的解决方法

    在 D3 中使用 d3.csv 读取 CSV 文件时,有时会出现乱码问题. 怎么解决呢? 1. 乱码问题 使用 d3.csv 读取 xxx.csv 文件时.假设 xxx.csv 文件使用的是 UTF- ...

  2. C#进阶系列 ---- 《CLR via C#》

      [C#进阶系列]30 学习总结 [C#进阶系列]29 混合线程同步构造 [C#进阶系列]28 基元线程同步构造 [C#进阶系列]27 I/O限制的异步操作 [C#进阶系列]26 计算限制的异步操作 ...

  3. C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解

    前言:已经有一个月没写点什么了,感觉心里空落落的.今天再来篇干货,想要学习Webapi的园友们速速动起来,跟着博主一起来学习吧.之前分享过一篇 C#进阶系列——WebApi接口传参不再困惑:传参详解  ...

  4. C#进阶系列——WebApi 接口参数不再困惑:传参详解

    前言:还记得刚使用WebApi那会儿,被它的传参机制折腾了好久,查阅了半天资料.如今,使用WebApi也有段时间了,今天就记录下API接口传参的一些方式方法,算是一个笔记,也希望能帮初学者少走弯路.本 ...

  5. C#进阶系列——WebApi 接口测试工具:WebApiTestClient

    前言:这两天在整WebApi的服务,由于调用方是Android客户端,Android开发人员也不懂C#语法,API里面的接口也不能直接给他们看,没办法,只有整个详细一点的文档呗.由于接口个数有点多,每 ...

  6. C#进阶系列——WebApi 跨域问题解决方案:CORS

    前言:上篇总结了下WebApi的接口测试工具的使用,这篇接着来看看WebAPI的另一个常见问题:跨域问题.本篇主要从实例的角度分享下CORS解决跨域问题一些细节. WebApi系列文章 C#进阶系列— ...

  7. C#进阶系列——WebApi 身份认证解决方案:Basic基础认证

    前言:最近,讨论到数据库安全的问题,于是就引出了WebApi服务没有加任何验证的问题.也就是说,任何人只要知道了接口的url,都能够模拟http请求去访问我们的服务接口,从而去增删改查数据库,这后果想 ...

  8. C#进阶系列——WebApi 异常处理解决方案

    前言:上篇C#进阶系列——WebApi接口传参不再困惑:传参详解介绍了WebApi参数的传递,这篇来看看WebApi里面异常的处理.关于异常处理,作为程序员的我们肯定不陌生,记得在介绍 AOP 的时候 ...

  9. C#进阶系列——WebApi 路由机制剖析:你准备好了吗?

    前言:从MVC到WebApi,路由机制一直是伴随着这些技术的一个重要组成部分. 它可以很简单:如果你仅仅只需要会用一些简单的路由,如/Home/Index,那么你只需要配置一个默认路由就能简单搞定: ...

  10. C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)

    前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...

随机推荐

  1. Linux连续执行多条命令

    引自:这里 每条命令使用";"隔开,则无论前边的命令执行成功与否都会继续执行下一条命令这里,故意将第二条命令中的echo多写了一个o,命令执行出错,但并不影响后续命令的执行可以这么 ...

  2. [原]quick2.25精灵变灰

    由于quick2.25没有导出shader相应的接口,所以2.25无法直接使用shader. 本文简单介绍如何导出相应接口,同时教大家使用shader 实现精灵变灰 一.编写静态函数,以供导出使用(直 ...

  3. Spring3系列3 -- JavaConfig

    Spring3系列3-JavaConfig-1 从Spring3开始,加入了JavaConfig特性,JavaConfig特性允许开发者不必在Spring的xml配置文件中定义bean,可以在Java ...

  4. HTML5新特性之WebRTC

    1.概述 WebRTC是“网络实时通信”(Web Real Time Communication)的缩写,它主要用来让浏览器实时获取和交换视频.音频和数据. WebRTC共分三个API. MediaS ...

  5. SQLite 入门教程 (终端)命令

    一.基本简介 SQLite 是一个自持的(self-contained).无服务器的.零配置的.事务型的关系型数据库引擎.因为他很小,所以也可以作为嵌入式数据库内建在你的应用程序中.SQLite 被应 ...

  6. CentOS 6上安装xfce桌面环境

    [日期:2012-01-30]   在新的CentOS 6上默认没有包含xfce的桌面环境,使用yum也找不到这些包,但是自己又喜欢这种简单的桌面环境,此时可以使用下面的方法来安装 [plain] $ ...

  7. NSDictionary 的有序性 (by the key in some rule)

    NSDictionary 的有序性: (by the key in some rule) NSDictionary*myDictionary =[NSDictionary dictionaryWith ...

  8. 【cs229-Lecture15】奇异值分解

    PCA的实现一般有两种,一种是用特征值分解去实现的,一种是用奇异值分解去实现的. 内容: PCA  (主成份分析)是一种直接的降维方法,通过求解特征值与特征向量,并选取特征值较大的一些特征向量来达到降 ...

  9. MyBatis XML 映射配置文件

    配置文件的基本结构 configuration —— 根元素 properties —— 定义配置外在化 settings —— 一些全局性的配置 typeAliases —— 为一些类定义别名 ty ...

  10. POJ 3345 Bribing FIPA 树形DP

    题目链接: POJ 3345 Bribing FIPA 题意: 一个国家要参加一个国际组织,  需要n个国家投票,  n个国家中有控制和被控制的关系, 形成了一颗树. 比如: 国家C被国家B控制, 国 ...