今天早上, 我读完了第二章, 感觉以上评论决不为过. 本书对 CLR等底层的讲述 之透彻, 绝非两句"wonderful"可以表述. 在此我想谈谈我对 CLR 在跨语言方面的理解.
首先 CLR的功能是建立在 CLR类型(CTS CommonTypeSystem) 的基础之上, 每种具体的.Net语言(如C#,J#等)的类型都是 CLR类型 的子集.
然后 当你用某种语言 写了一段代码后,有各种语言的编译器 把其编译成IL(例如csc.exe 把你的 .cs文件编译成能够受 CLR 支持的IL.) 无论你在某种语言使用的何种数据类型,变量,方法,事件 都会在IL表示为 属性和操作(在IL中只有这两种表示)
最后 当你IL的这种 属性和操作 是都能够被 各种语言所识别使用的.(这样就避免的C#的类型不能被VB.net识别的问题了)
昨天晚上,看了一下 class和struct的比较.简单谈一下自己的体会
在本书中,首先谈到在c++等语言中是有struct的.而Java是一个精简基本类的编程语言,Java没有提供struct类型(字所有去处这种类型,是因为struct完全可以有class来实现).
然后又谈到c# 在很大程度上参考了Java(这个还又说,地球人都知道了:)).但是又把struct从c++中托了回来, 这究竟为什么呢?
原来struct是值类型使用,而class是引用类型使用.建立struct的时候是在栈上分配内存,而class是在托管堆上分配内存. 这样差别就出来了: 当向栈上push的时候分配一块内存,当从栈上pop的时候内存自动释放. 而在clr的托管堆上就不同了,当你new一个class成一个object的时候分配一块内存. 但是当你使用完本对象销毁的时候,内存空间是 不能实时释放的.要等GC不定时间进行收集. 这个内存收集过程又会占有很大一部分资源(关于内存收集方面的内容,本论坛内有其他几篇文章论述).
所有出于性能方面的考虑 我们在写程序的时候能构使用struct的地方,就尽量不要使用class
关于类型转换中的 is和as的使用
is作为运算符出现在类型转换中的.其作用是检查对象的运行时类型是否与给定类型兼容,在这种类型兼容性判定中要进行一次类型转换. 然后根据类型的兼容性进行其他处理工作.
AS 也是一个运算符, 对某个对象进行 as操作后 分为两种情况 1.如果类型兼容 操作成功 就会返回 as后的新类型 2.如果操作失败就会返回null.
这样就可以看出两操作符的 性能比较了.比如我原来 使用FindControl()方法 根据一个 变量名称的string 去获取这个变量时 是这么写的:
if(this.FindControl("myCheck") is System.CheckBox) //M处
{
System.CheckBox tempCheck= (System.CheckBox)this.FindControl("myCheck");//N处
tempCheck.Checked= true;
}
很明显以上操作在M和N处分别进行了 两次类型转换.
如果现在改成这么写
System.CheckBox tempCheck= this.FindControl("myCheck") as System.CheckBox;//L处
if(tempCheck!=null
{
tempCheck.Checked= true;
}
现在就只是在L处 进行了一次类型转换.
类型转换在CLR中,是比较消耗性能的, 现在大家应该知道怎么办了吧.(至少我以后不会用第一中方法了)
最近几天晚上 一直都在喝酒,没有空看书. 昨晚好不容易看了一点关于 类构造器得问题(同时 前些天也跟刘**谈论过这个问题).那就是一个类可不可以没有构造函数. 先看这段代码: using System;
namespace Xieran.Test
{
public class App
{
static public void Main(System.String[] args)
{
System.Console.WriteLine("Hi,Mr.Liuxd!");
System.Console.ReadLine();
}
}
}
其编译是可以通过 并且可以得到预想得答案数据. 好像一个类没有构造函数也可以得. 难道这是真得么? 通过ILDasm查看IL 代码如下:
可以看到 .ctor():void ,这个便是IL里面得类得构造函数. 呵呵 原来是CSC在编译我们代码得时候 自动加了一个 构造函数. 自此,一个类得构造函数是不是必须得 就不言自明
关于值类型和引用类型的理解
.NET CLR中有两种数据类型 值类型和引用类型. 其中值类型是一种简单的数据类型 其是在 应用程序的线程栈上进行分配和即时内存回收的. 引用类型 是在CLR托管堆上进行内存分配的, 同时其在托管堆上分配的地址被记录在 应用程序的线程栈上, 最后他的内存回收不是即时的 其是有GC进行不定时的收集.
.NET中所有的类型 都继承于System.Object, 当然值类型也是如此. 不过值类型是通过继承ValueType类型间接的继承于Sytem.Object. 继承或间接继承于System.Object的类都是 引用类型(有特例) 包括ValueType类型. 但是经过ValueType类型的处理(具体怎么处理的我也不清楚:( ),继承于ValueType的类 便成了 值类型(有点奇怪 从引用类型继承下来的东西成了 值类型 :( ). 而有ValueType继承得到的值类型, 便不内为其他值类型的基类了,也就是说所有的值类型 都是直接继承于 ValueType.(值类型不能被继承着个也不奇怪, 继承是引用类型的专利 因为子类在获取父类的方法 属性的时候 需要使用的 应用表 只有在引用类型中才有的. )
关于String和StringBuilder的使用
前些天作一个项目 需要安装客户提供的Excel样式,打印输入同样的报表(初接项目时 巨汗),后来与同事探讨打算 以原Excel作为模板, 然后读入此Excel的全部内容(包括样式等信息) 在程序后台 对此Excel每个cell内的内容 通过读取数据库进行替换. 最后再把替换后的 Excel 文件 通过Response刷向客户端. ----呵呵,这绝对是一个很好的创意(否则,如何才能保证和客户要求的Excel样式完全一样?).
写了个模板读取引擎(其专门负责读取客户提供的Excel文件) ,最初 读取后的数据存放在一个string中,然后每次 利用string的Replace方法逐一替换数值. 要知道客户交给我们的Excel文件有5个多k大小. 其中要替换900次. 这个有点危言耸听把 呵呵. 当我还没有替换完成 系统就崩溃了. 有副图片大家可以看看
内存从300多M一路上杨到800多M, 然后突然崩溃(就在内存突然降低处).
看来这个方法不行. 然后想到了StringBuilder, 这次把模板引擎读取的数据直接保持在StringBuilder中,然后调用StringBuilder的Replace方法进行 这900次替换. 呵呵 看看内存的使用情况把
从图上可以清楚地看出在cpu100%运行的地方内存甚至还有所下降(不知道为什么会下降,我执行了号几次都是内存会下降 --我怀疑每每在此时都有内存收集).
现在改轮到解释一下 这种现象的本质了:
string对象一旦创建 便会在内存中”稳定”的存在.他的大小长度永远不会被改变. 比如你使用
string s=”Hello”;
s.ToLower();
那么会是这杨,首先在内存中创建一个字符川s, 然后当你ToLower()的时候, 一个小写的”hello”会是重新分配一块内存, 而这块内存与 小s占用内存已经没有任何关系了. 在我的第一段程序中,这900次替换 每次都要 重新分配一次内存 . 而分配内存的过程是要好用时间的. 同时新内存分配后,旧内存并没有实时释放, 所以会出现 系统崩溃的情形了.
而StringBuilder就不同了, StringBuilder每次Replace的时候 都是在他自己本身上进行的. 不需要另外分配内存 这样季不占有内存分配时的时间 也不占有内存空间.所以会出现 下面一副 比较”赏心悦目”的图了.
由此可见,操作大的字符串,尤其是 在字符串上频繁的有操作时 尤其应该使用StringBuilder了
可以看到除了增加了一个静态的变量myGame外,csc编译器还自动增加了一个类型构造器(.cctor---class constructor). 打开此类型构造器看到IL如下:
.method private hidebysig specialname rtspecialname static
void .cctor() cil managed
{
// 代码大小 11 (0xb)
.maxstack 1
IL_0000: ldstr "footBall"
IL_0005: stsfld string xieran.Test.myClass::myGame
IL_000a: ret
} // end of method myClass::.cctor
也就是说在代码中声明的静态字段 是在类型构造器中初始化的, 声明的const显然没有在类型构造器中初始化.
在myName上双击打开其代码 为
.field private static literal string myName = "Liuxd"
在myGame上打开其代码为
.field private static string myGame
也就是说常量是在编译时确定其值的. 各种变量(包括static变量)是在运行时 通过构造器(实例构造器或类型构造器)进行初始化的. 常量在编译后其值是放在模块的元数据中的. 同时由于常数是在编译时初始化,所有其 能够支持的数据类型也是有限的(其类型必须是编译器认识的“基元类型“如Int32,String等,至于其他的各种类型就不支持了).
关于String和StringBuilder的使用
前些天作一个项目 需要安装客户提供的Excel样式,打印输入同样的报表(初接项目时 巨汗),后来与同事探讨打算 以原Excel作为模板, 然后读入此Excel的全部内容(包括样式等信息) 在程序后台 对此Excel每个cell内的内容 通过读取数据库进行替换. 最后再把替换后的 Excel 文件 通过Response刷向客户端. ----呵呵,这绝对是一个很好的创意(否则,如何才能保证和客户要求的Excel样式完全一样?).
写了个模板读取引擎(其专门负责读取客户提供的Excel文件) ,最初 读取后的数据存放在一个string中,然后每次 利用string的Replace方法逐一替换数值. 要知道客户交给我们的Excel文件有5个多k大小. 其中要替换900次. 这个有点危言耸听把 呵呵. 当我还没有替换完成 系统就崩溃了. 有副图片大家可以看看
内存从300多M一路上杨到800多M, 然后突然崩溃(就在内存突然降低处).
看来这个方法不行. 然后想到了StringBuilder, 这次把模板引擎读取的数据直接保持在StringBuilder中,然后调用StringBuilder的Replace方法进行 这900次替换. 呵呵 看看内存的使用情况把
从图上可以清楚地看出在cpu100%运行的地方内存甚至还有所下降(不知道为什么会下降,我执行了号几次都是内存会下降 --我怀疑每每在此时都有内存收集).
现在改轮到解释一下 这种现象的本质了:
string对象一旦创建 便会在内存中”稳定”的存在.他的大小长度永远不会被改变. 比如你使用
string s=”Hello”;
s.ToLower();
那么会是这杨,首先在内存中创建一个字符川s, 然后当你ToLower()的时候, 一个小写的”hello”会是重新分配一块内存, 而这块内存与 小s占用内存已经没有任何关系了. 在我的第一段程序中,这900次替换 每次都要 重新分配一次内存 . 而分配内存的过程是要好用时间的. 同时新内存分配后,旧内存并没有实时释放, 所以会出现 系统崩溃的情形了.
而StringBuilder就不同了, StringBuilder每次Replace的时候 都是在他自己本身上进行的. 不需要另外分配内存 这样季不占有内存分配时的时间 也不占有内存空间.所以会出现 下面一副 比较”赏心悦目”的图了.
由此可见,操作大的字符串,尤其是 在字符串上频繁的有操作时 尤其应该使用StringBuilder了
可以看到除了增加了一个静态的变量myGame外,csc编译器还自动增加了一个类型构造器(.cctor---class constructor). 打开此类型构造器看到IL如下:
.method private hidebysig specialname rtspecialname static
void .cctor() cil managed
{
// 代码大小 11 (0xb)
.maxstack 1
IL_0000: ldstr "footBall"
IL_0005: stsfld string xieran.Test.myClass::myGame
IL_000a: ret
} // end of method myClass::.cctor
也就是说在代码中声明的静态字段 是在类型构造器中初始化的, 声明的const显然没有在类型构造器中初始化.
在myName上双击打开其代码 为
.field private static literal string myName = "Liuxd"
在myGame上打开其代码为
.field private static string myGame
也就是说常量是在编译时确定其值的. 各种变量(包括static变量)是在运行时 通过构造器(实例构造器或类型构造器)进行初始化的. 常量在编译后其值是放在模块的元数据中的. 同时由于常数是在编译时初始化,所有其 能够支持的数据类型也是有限的(其类型必须是编译器认识的“基元类型“如Int32,String等,至于其他的各种类型就不支持了).
关于值类型和引用类型的理解
.NET CLR中有两种数据类型 值类型和引用类型. 其中值类型是一种简单的数据类型 其是在 应用程序的线程栈上进行分配和即时内存回收的. 引用类型 是在CLR托管堆上进行内存分配的, 同时其在托管堆上分配的地址被记录在 应用程序的线程栈上, 最后他的内存回收不是即时的 其是有GC进行不定时的收集.
.NET中所有的类型 都继承于System.Object, 当然值类型也是如此. 不过值类型是通过继承ValueType类型间接的继承于Sytem.Object. 继承或间接继承于System.Object的类都是 引用类型(有特例) 包括ValueType类型. 但是经过ValueType类型的处理(具体怎么处理的我也不清楚:( ),继承于ValueType的类 便成了 值类型(有点奇怪 从引用类型继承下来的东西成了 值类型 :( ). 而有ValueType继承得到的值类型, 便不内为其他值类型的基类了,也就是说所有的值类型 都是直接继承于 ValueType.(值类型不能被继承着个也不奇怪, 继承是引用类型的专利 因为子类在获取父类的方法 属性的时候 需要使用的 应用表 只有在引用类型中才有的. )
关于String和StringBuilder的使用
前些天作一个项目 需要安装客户提供的Excel样式,打印输入同样的报表(初接项目时 巨汗),后来与同事探讨打算 以原Excel作为模板, 然后读入此Excel的全部内容(包括样式等信息) 在程序后台 对此Excel每个cell内的内容 通过读取数据库进行替换. 最后再把替换后的 Excel 文件 通过Response刷向客户端. ----呵呵,这绝对是一个很好的创意(否则,如何才能保证和客户要求的Excel样式完全一样?).
写了个模板读取引擎(其专门负责读取客户提供的Excel文件) ,最初 读取后的数据存放在一个string中,然后每次 利用string的Replace方法逐一替换数值. 要知道客户交给我们的Excel文件有5个多k大小. 其中要替换900次. 这个有点危言耸听把 呵呵. 当我还没有替换完成 系统就崩溃了. 有副图片大家可以看看
内存从300多M一路上杨到800多M, 然后突然崩溃(就在内存突然降低处).
看来这个方法不行. 然后想到了StringBuilder, 这次把模板引擎读取的数据直接保持在StringBuilder中,然后调用StringBuilder的Replace方法进行 这900次替换. 呵呵 看看内存的使用情况把
从图上可以清楚地看出在cpu100%运行的地方内存甚至还有所下降(不知道为什么会下降,我执行了号几次都是内存会下降 --我怀疑每每在此时都有内存收集).
现在改轮到解释一下 这种现象的本质了:
string对象一旦创建 便会在内存中”稳定”的存在.他的大小长度永远不会被改变. 比如你使用
string s=”Hello”;
s.ToLower();
那么会是这杨,首先在内存中创建一个字符川s, 然后当你ToLower()的时候, 一个小写的”hello”会是重新分配一块内存, 而这块内存与 小s占用内存已经没有任何关系了. 在我的第一段程序中,这900次替换 每次都要 重新分配一次内存 . 而分配内存的过程是要好用时间的. 同时新内存分配后,旧内存并没有实时释放, 所以会出现 系统崩溃的情形了.
而StringBuilder就不同了, StringBuilder每次Replace的时候 都是在他自己本身上进行的. 不需要另外分配内存 这样季不占有内存分配时的时间 也不占有内存空间.所以会出现 下面一副 比较”赏心悦目”的图了.
由此可见,操作大的字符串,尤其是 在字符串上频繁的有操作时 尤其应该使用StringBuilder了
可以看到除了增加了一个静态的变量myGame外,csc编译器还自动增加了一个类型构造器(.cctor---class constructor). 打开此类型构造器看到IL如下:
.method private hidebysig specialname rtspecialname static
void .cctor() cil managed
{
// 代码大小 11 (0xb)
.maxstack 1
IL_0000: ldstr "footBall"
IL_0005: stsfld string xieran.Test.myClass::myGame
IL_000a: ret
} // end of method myClass::.cctor
也就是说在代码中声明的静态字段 是在类型构造器中初始化的, 声明的const显然没有在类型构造器中初始化.
在myName上双击打开其代码 为
.field private static literal string myName = "Liuxd"
在myGame上打开其代码为
.field private static string myGame
也就是说常量是在编译时确定其值的. 各种变量(包括static变量)是在运行时 通过构造器(实例构造器或类型构造器)进行初始化的. 常量在编译后其值是放在模块的元数据中的. 同时由于常数是在编译时初始化,所有其 能够支持的数据类型也是有限的(其类型必须是编译器认识的“基元类型“如Int32,String等,至于其他的各种类型就不支持了).
关于String和StringBuilder的使用
前些天作一个项目 需要安装客户提供的Excel样式,打印输入同样的报表(初接项目时 巨汗),后来与同事探讨打算 以原Excel作为模板, 然后读入此Excel的全部内容(包括样式等信息) 在程序后台 对此Excel每个cell内的内容 通过读取数据库进行替换. 最后再把替换后的 Excel 文件 通过Response刷向客户端. ----呵呵,这绝对是一个很好的创意(否则,如何才能保证和客户要求的Excel样式完全一样?).
写了个模板读取引擎(其专门负责读取客户提供的Excel文件) ,最初 读取后的数据存放在一个string中,然后每次 利用string的Replace方法逐一替换数值. 要知道客户交给我们的Excel文件有5个多k大小. 其中要替换900次. 这个有点危言耸听把 呵呵. 当我还没有替换完成 系统就崩溃了. 有副图片大家可以看看
内存从300多M一路上杨到800多M, 然后突然崩溃(就在内存突然降低处).
看来这个方法不行. 然后想到了StringBuilder, 这次把模板引擎读取的数据直接保持在StringBuilder中,然后调用StringBuilder的Replace方法进行 这900次替换. 呵呵 看看内存的使用情况把
从图上可以清楚地看出在cpu100%运行的地方内存甚至还有所下降(不知道为什么会下降,我执行了号几次都是内存会下降 --我怀疑每每在此时都有内存收集).
现在改轮到解释一下 这种现象的本质了:
string对象一旦创建 便会在内存中”稳定”的存在.他的大小长度永远不会被改变. 比如你使用
string s=”Hello”;
s.ToLower();
那么会是这杨,首先在内存中创建一个字符川s, 然后当你ToLower()的时候, 一个小写的”hello”会是重新分配一块内存, 而这块内存与 小s占用内存已经没有任何关系了. 在我的第一段程序中,这900次替换 每次都要 重新分配一次内存 . 而分配内存的过程是要好用时间的. 同时新内存分配后,旧内存并没有实时释放, 所以会出现 系统崩溃的情形了.
而StringBuilder就不同了, StringBuilder每次Replace的时候 都是在他自己本身上进行的. 不需要另外分配内存 这样季不占有内存分配时的时间 也不占有内存空间.所以会出现 下面一副 比较”赏心悦目”的图了.
由此可见,操作大的字符串,尤其是 在字符串上频繁的有操作时 尤其应该使用StringBuilder了
可以看到除了增加了一个静态的变量myGame外,csc编译器还自动增加了一个类型构造器(.cctor---class constructor). 打开此类型构造器看到IL如下:
.method private hidebysig specialname rtspecialname static
void .cctor() cil managed
{
// 代码大小 11 (0xb)
.maxstack 1
IL_0000: ldstr "footBall"
IL_0005: stsfld string xieran.Test.myClass::myGame
IL_000a: ret
} // end of method myClass::.cctor
也就是说在代码中声明的静态字段 是在类型构造器中初始化的, 声明的const显然没有在类型构造器中初始化.
在myName上双击打开其代码 为
.field private static literal string myName = "Liuxd"
在myGame上打开其代码为
.field private static string myGame
也就是说常量是在编译时确定其值的. 各种变量(包括static变量)是在运行时 通过构造器(实例构造器或类型构造器)进行初始化的. 常量在编译后其值是放在模块的元数据中的. 同时由于常数是在编译时初始化,所有其 能够支持的数据类型也是有限的(其类型必须是编译器认识的“基元类型“如Int32,String等,至于其他的各种类型就不支持了).