Breathe李

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  13 随笔 :: 0 文章 :: 48 评论 :: 0 引用

公告

2012年4月20日 #

    最近从winform转到web做开发,有很多东西都没有学过或者好久不用都忘记了,自己一直在补课,至于CLR这个系列又不知道要拖多久了,估计一年之后才能写完吧,o(∩_∩)o 。要学习JQuery,就要先学习JS,JS还要从基础开始学,这样能理解的更透彻一些,至少我是这样认为的。笔者从来都鄙视那些在编写代码的时候,从网上找到现成的代码,直接Ctrl+C、V就完成了的编码工作者,哪怕是你照着网上的例子打一遍,完完全全一样的,自己的理解也会更透彻一些吧,直到自己举一反三。呵呵,好了,言归正传。

    这篇博文实在工作、学习之余写的一篇,可能内容不会很长,但是我觉得还是有必要说一下。

    首先,在我们编写一个新的类型的时候,我们会考虑这个类是要公开的(public)还是只限程序集内访问的(internal)类型。我们都知道,如果我们定为public的话,这个类型,将在整个程序集内、外都可访问的。internal,则是只能在定义该类型的程序集中才能访问到。那么, 我们只想让我们指定的几个程序集访问的话 ,但是我们又不想定义为public的类型,怎么办?在这种情况下就可以用到“友元程序集”这个特性了 (暂且说为是特性,具体的我也不知道怎么表达了)

   友元程序集应该怎么使用呢?废话少说,开始上代码:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Runtime.CompilerServices;
 4 
 5 [assembly: InternalsVisibleTo("ConsoleApplication2")]
 6 namespace TestFriendSpace
 7 {
 8     public class Test{}
 9     internal class MyArrayList
10     {
11         //do someth........
12     }
13 }

上面的代码可以看到,我在namespace上面加入了一个[assembly: InternalsVisibleTo("ConsoleApplication2")]的标记,这句话就是告诉编译器我们这个程序集中的internal类,可以让ConsoleApplication2 使用我们在namespace中定义的internal类中的方法,当然也包括internal标记的方法。

  当然,这个特征看起来比较新鲜,至少我之前真的没用到过。在使用这个特征之前,我们还需要注意的一点就是说,友元程序集的耦合度非常的高,如果版本的不同,很可能会引起程序的不兼容的问题。所以,在遇到上面问题的时候,最好还是要设置成public 这样可以避免一些不必要的麻烦。

     虽然这个特性的用处不大,但是,保不准哪天我们就需要这样做,但是一定要记住这个方法使程序集之间的耦合度非常高(友元程序集之间),如果能保证友元程序集的版本一直,那用起来完全没有问题的。至少能在别人拿到我们编写的dll的时候,不是友元程序集就无法使用,当然,在这里我只是用了一个友元程序集的名称,如果真的想限定友元程序集,可以加入第二个参数为程序集的公匙,这样就完全限定了友元程序集。

 

  这篇文章也就结束了,之所以记录这样一个特征,只是觉得好玩而已,.net给了我们太多的乐趣,还有很多令人觉的好玩的特征,是等待我们去发现的。

posted @ 2012-04-20 21:43 LouisLee 阅读(11) 评论(0) 编辑

2011年12月28日 #

值类型和引用类型、装箱与拆箱

       本想把这篇博文题目的拆开来说,但是想一想,值类型和引用类型、装箱与拆箱又是密不可分的,于是决定还是放在一起来说。

一、  值类型和引用类型:

在我们刚开始学习写程序的时候,面向对象的三大概念等都是我们所能熟悉,并且比较好理解的概念,但是,到值类型和引用类型的时候,我相信有大部分的同仁都曾经迷茫过(包括我^_^)

在我们之前说的基元类型中,无非分为两大类型,一个就是值类型,另一个则是引用类型。我们先说一下引用类型,首先,我们需要非常明确的一点就是引用类型是从托管堆上面分配空间的而值类型是在一个线程堆栈上分配空间的(值类型变量做为局部变量时,该实例将被创建在堆栈上;而如果值类型变量作为类型的成员变量时,它将作为类型实例数据的一部分,同该类型的其他字段都保存在托管堆上)。当我们用new关键字去构建一个引用类型的时候,new操作符返回给我们的是一个对象数据的内存地址,我们也可以理解为一个对象指针。CLR会对我们构建的引用类型对象的成员进行初始化的操作并将引用类型对象中的字段进行初始化操作为0或者null,还有一点我们必须注意的是,在我们构建完一个引用类型的时候,很可能会增加一次GC(垃圾回收)的操作,就是说,当我们这个引用类型的对象被认为不会再使用的时候,将被垃圾回收,以释放占用的内存空间(关于垃圾回收,将在以后的博文中进行讨论)然而值类型是在现成堆栈上进行分配的,所以它没有一个对象指针,我们操作值类型就是操作值类型中的数据。并且,值类型不受GC垃圾回收的制约。我们来举个例子,并将一步一步的进行分析。代码如下:

static void Main(string[] args)
{
String str_a = "abc";//在托管堆上分配内存
Int32 int_a = 123;//在线程堆栈上分配内存
String str_b = str_a;//复制str_a的对象指针
Int32 int_b = int_a;//在线程堆栈上分配内存并复制成员
Console.WriteLine(str_a);//打印str_a的值为abc
Console.WriteLine(str_b);//打印str_b的值为abc
//这里不再打印int_a和int_b的值
str_b = "uio";
int_b = 456;
Console.WriteLine(str_a);//打印str_a的值为abc
Console.WriteLine(str_b);//打印str_b的值为uio
Console.WriteLine(int_a);//打印int_a的值为123
Console.WriteLine(int_b);//打印int_b的值为456
Console.ReadLine();
}

通过以上代码我们可以看到,当我们分别从托管堆上分配内存给str_a的时候str_a储存的并不是“abc”而是”abc”的内存地址,同样将str_a的值赋给str_b的时候,str_b储存的并不是”abc”而是从str_a那里复制到”abc”的内存地址,所以,我们第一次打印str_a和str_b的时候,打印出来的结果是一样的,都是”abc”。然而,当我们把str_b的值更改为”uio”的时候,str_b储存的内存地址就为”uio”的内存地址,而str_a没有做任何改变,所以,第二次打印str_a和str_b的时候,结果为”abc”和“uio”。

       那么,值类型是怎么回事呢?值类型存储中,并不包含一个对象的指针,而是它本身的一个字段,也就是说当int_a赋值给int_b的时候,int_b把int_a的所有成员全部进行复制在一个新的内存空间,而int_b的修改操作,并不影响int_a。

       以上为笔者对值类型和引用类型的理解,有什么错误的地方也请大家指出。

一、  装箱和拆箱

下面我们来讨论下装箱和拆箱,装箱和拆箱是指:从值类型转换成引用类型的操作,我们称之为装箱,反之,从引用类型转换成值类型,我们称之为拆箱操作。

       理论上,这两句话应该很好理解,实际上其实也不难。我们来看一个例子                 

如下:

static void Main(string[] args)
{
Int32 int_a = 5;
object o = int_a;
int_a = 123;
o = 456;
Console.WriteLine(int_a+","+(Int32)o);
Console.ReadLine();
}

我们来分析下这段代码执行了几次装箱和几次拆箱。首先,我们给int_a赋值为5,并且把int_a赋值给object类型,这个时候就发生了一次装箱操作,就是说把int_a的值5,分配相应的内存复制到托管堆上,并把值为5的内存地址指针返回给object类型,这是一次装箱操作,在装箱操作的时候,int_a还在线程堆栈上并且值为5。Int_a=123只是单纯的更改了原来值。o=456这里面又进行了一次装箱操作,就是说int类型456装箱成引用类型(操作与o=int_a一样),并把o的指针引用更改为456的指针引用。之前123将等待垃圾回收。接下来,看打印输出的方法。在这里面,我们其实传入的参数都为String类型,所以在这里int_a也执行了装箱操作,因为与String类型的“,”进行连接操作。还有一个装箱操作就是在(Int32)o这里面,我们将o强制类型转换是一个拆箱操作(引用类型转换成值类型为拆箱操作),这也是整个Main方法中的唯一一次拆箱操作,那么在转换成值类型之后,又与String类型的”,”进行连接操作,在这里就又执行了一次装箱操作。最后返回给WriteLine方法的是一个字符串。打印出来为“123,456”所以上述代码进行了4次装箱操作和1次拆箱操作。为了更清晰的表现给大家,直接上IL代码的图:

 

装箱操作我已经使用方框标识出来,拆箱操作我已经使用横线标识出来。

       对于装箱与拆箱操作,上述代码可以看出,为了提高上段代码的效率,我们没有必要在执行WriteLine方法的时候对o进行一次拆箱操作。至于为什么这么写只是为了更清晰的演示装箱与拆箱操作在编译为IL代码的时候是如何表示的。

       我想这篇博文也到此结束了,可能没有很深入的讨论到值类型与引用类型、装箱和拆箱操作,只是点到为止吧,更深层次的东西,也希望大家能够自己进行一个研究。必要的时候看一下IL代码,会了解到很多我们之前不知道的东西。如有错误、问题也请各位看官留言告诉我。谢谢!

      

 

       在这里再次为论坛做个宣传 www.175m.com,希望大家可以在论坛里面一起建设、讨论该论坛(希望博客园的大哥们,不要以为我在挖墙角,我只是在为我们程序员的大家庭做贡献)



posted @ 2011-12-28 10:43 LouisLee 阅读(1005) 评论(7) 编辑

2011年12月27日 #

     今天这篇文章会不是很长,主要是给大家介绍一下我们CLR中的一些基元类型,最近年底,确实比较忙。希望大家理解下。

     记得我刚开始学会写程序的时候一直都闹不明白,什么时候要用string而什么时候又要用String(当然,我是半路出家的当时基础只是也不是很好,大侠们就不要喷我了吧 ^ _ ^)当过了一段时间时候,自己又实验了一下感觉貌似没什么区别,又查找了写资料,发现我们在C# 中可以使用string关键字是因为编译器会把我写的string的类型,对应到System.String类型,加了string关键字只是为了让我们在写代码的时候方便阅读。当然,int和System.Int32 也是一样的。

     在我们C#中有很多基元类型byte,,short,int,double,long,float,bool等。它们都对应自己相应的FCL类型,但是都有对应的FCL类型,是否在.net平台上所有的语言都可以解析呢?答案是否定的。每个语言都有每个语言的特性,比如在C#中,int类型对应的是有符号32位整数值,long类型对应的是有符号64位整数值,但是在C++中,int类型对应的是有符号16为整数值,而long是对应的有符号32位整数值。之前我给大家提到过,如果要在.net平台中用C#写的类库能使其它语言使用,必须遵守一个标准CLS(详情见:初读CLR Via C# 之 IL、CTS、CLS),只有在遵守了这个标准之后才能实现.net平台下的无缝移植。

    在书中,还有checeked/unchecked代码段的介绍,但是在笔者的实际开发中,确实没有用到过checked和unchecked代码段,所以在这里就不啰嗦了,如果读者有兴趣,可以百度一下。

    那么在这里,我还要说一下另一个基元类型,System.Decimal,该类型为128位有符号高精度浮点值,但是在CLR中,Decimal类型并不被认为是基元类型,也就是说,Decimal在被定义的时候,它就是特殊的。我们通过文档可以看到Decimal对我们的所有操作符都进行了重载,如图:

我们可以看下Addition的语法,如图:

那么,我们接下来可以看下在IL代码下,Decimal是怎样表示的,首先贴代码:

 static void Main(string[] args)
        {
            Decimal da = new Decimal(2);
            Decimal db = 3;
            Decimal dc = da + db;
        }

  然后是IL代码:

 

 通过上图我们可以看到,Decimal在编译之后进行求和的IL的代码,并没有一个很简介的IL代码,来表示一个求和的操作,而是通过方法。那么我们再看下int类型编译之后求和的IL代码:

    通过这两张图进行比较,我们应该很轻易就能想到,CLR在进行处理基元类型的时候,Decimal的处理速度慢于处理int基元类型的速度。那么,在这里提醒大家一句,如果你要使用checked/unchecked代码段,对Decimal是无效的,因为在IL代码中,没有相应的IL代码来表示Decimal的加、减、乘、除等操作。

    以上为笔者对C# 基元类型的理解。如有不对的地方,请指出。下一章,笔者将写一下值类型和引用类型、装箱和拆箱的理解。

 

    再为群里面的论坛做一个宣传,论坛地址为:www.175m.com  希望大家可以在论坛里面一起建设、讨论该论坛(希望博客园的大哥们,不要以为我在挖墙角,我只是在为我们程序员的大家庭做贡献)

posted @ 2011-12-27 10:41 LouisLee 阅读(1206) 评论(4) 编辑

2011年12月7日 #

在这篇博文中,可能要说的东西比较难,笔者本来准备前天就进行更新的,但是反复的斟酌一直找不到合适的语句去表述,也可能笔者自己理解的也不是很透彻,所以,在这篇博文中,有不对的地方,望广大网友指出,并给予纠正,大家共同进步嘛,下面开始。

在我们写完一个程序模块的时候,在提交给PM进行检验的时候,自己都会先运行一下找一些BUG进行测试,并改正。但是,在我们运行调试模块的时候,CLR到底是怎么样进行加载程序的呢?

当CLR开始加载一个进程之后,在进程中会有很多线程,当一个线程创建的时候,CLR会自动分配1MB的堆栈空间,并把当前方法所需要的参数、方法内部定义的局部变量进行储存,并负责传递下一个方法的参数。当JIT编译该方法为本地CPU指令前,CLR要确保该方法中定义的所有程序集都加载到当前的APPDomain中,并将该方法中的所有局部变量进行一个初始化的操作(将变量初始化为null或者0)然后,CLR利用程序集的元数据来提取类型的信息。说到这里,就又引出一些知识,CLR是如何知道这些类型对象里面有什么方法和属性的呢?在我们进行编译的时候,没一个类型都对应了三张表,在下只介绍元数据定义表,因为篇幅太长

元数据定义表:

在编译器编译源代码时,代码中的任何一样都会在这个表中插入一条相应的记录。该表中包含:

1、  ModuleDef:包含模块的标示记录,包括文件名和扩展名,并由GUID生成的一个ID;

2、  TypeDef:包含模块中任意一个定义的类型名称、基类、标示(public\private等),以及指向MethodDef、FieldDef、ParamDef、PropertyDef以及EventDef的索引;

3、  MethodDef:包含模块中定义的方法的名称、标示(private public等)以及指向ParamDef的索引;

4、  FieldDef:包含模块中定义的每个字段的名称、标示(public\private等)、类型;

5、  ParamDef:包含模块中定义的每个参数的名称、标示(in\out\ref)、类型;

6、  PropertyDef:包含模块中定义的每个属性的名称、标示、类型;

7、  EventDef:包含模块中定义的每个时间的名称和标识

这就是元数据定义表的大概的一个组成部分。还有其他两张表,在这里就不一一介绍了。

笔者认为CLR就是通过这张表中,找到对象引用需要的所有程序集的元数据的并进行加载、压入到堆栈空间中并执行的。那么具体执行的是怎么一回事呢?由于笔者怕自己的理解有误,画出的图更不敢粘贴在博文中,怕误导,所以拿出原书中的图,笔者将在图下写出笔者的见解,望各位见谅!首先我们假设有两个类,如下:

internal class Employee

{

public Int32 GetYearsEmploye()

{

return -1;

}

public virtual String GenProcessReport()

{

return string.Empty;

}



public static Employee LockUp(String name)

{

return null;

}

}



internal class Manager : Employee

{

public override string GenProcessReport()

{

return base.GenProcessReport();

}

}



  假设当Windows进程启动之后,CLR也已经加载需要的程序集元数据并且也对堆栈进行了初始化并对当前线程分配了1MB的堆栈空间。如图:

  大家看到的线程堆栈中有阴影的部分,我们可以理解为该线程已经执行了部分代码,马上要开始执行M3方法。根据CLR的运行机制,首先需要JIT将M3方法编译成本地的CPU指令,与此同时,CLR要确保APPDomain中已经加载了M3方法中的所有类型。然后通过元数据定义表找到M3需要的所有相关信息,并在堆上面构建一个数据表来表示Employee、Manager类型的对象,如图:

  这个时候,CLR确定方法所需要的所有类型都已经创建好之后,并M3方法的代码编译完成之后,准许执行代码。在执行代码的过程中,CLR都会自动初始化对象的成员,并初始化对象实例的字段为null或者为0,这些都做完之后,才会执行我们在代码中所写的构造函数(在上一篇文章中,我给大家介绍了如何创建对象实例)如图:

    上图表示的是已经调用了Employee的构造器,并在堆中创建实例,类型对象指针返回对象内存地址。

  那么接下来,我们代码需要执行一个静态方法为“Lookup”,当这个方法执行时,CLR会定位到静态方法所属的类型,然后通过JIT进行编译(如果JIT已经编译好则无需再次编译)。Lockup方法会在内部再次创建一个新的Manager对象,返回该对象的地址,并由e局部变量进行保存。如图:

  在这个,e保存的对象地址不再是之前的Manager对象的地址,这个时候的地址为Employee的对象地址,而之前的Manager对象不再有对象进行引用,等待GC进行回收,而且它成为GC回收的主要对象。在执行到year=e.GetYearsEmployed();方法的时候,CLR同样要找到该方法的记录项(笔者认为在元数据定义表中)然后JIT进行编译(如果已经编译过JIT无需再次编译)并返回数据给year变量,如图:

  最后一步,当我们运行到e.GetProgressReport()这个方法的时候,CLR将根据方法查找相应的对象,这个时候e变量又指向Manager对象,因为在Employee对象中的GetProgressReport()虚方法的最终实现在Manager类中。CLR同样要找到该方法的记录项(笔者认为在元数据定义表中)然后JIT进行编译(如果已经编译过JIT无需再次编译)。

 

   至此,关于程序运行时的关系,是笔者的见解,如有错误的地方,请各位园友之处,并给予纠正。谢谢!

posted @ 2011-12-07 13:52 LouisLee 阅读(827) 评论(0) 编辑

2011年12月5日 #

     最近由于出差在外地,再加上自己在学习一些新的知识,所以迟迟没有再看这本书,更没有更新此系列的后续内容,但是这本书确实是本好书,不想就此放下,碰巧今天也比较悠闲,于是今天又一次回顾了之前的内容,并开始了新的阅读。今天开始尽量尽快进行更新。

     在开始之前,我想先讲一下继承的概念。大家都知道,面向对象是由:封装、继承、多态来体现的,至于什么是封装,不是很明白的朋友请去Google找下答案吧。那么,什么事继承?“继承”我们可以理解为是一种在无需重新编写类本身的情况下,对该类进行扩展的能力,并且可以使用现有类的所有功能。那么,通过继承创建的新类,我们可以称之为:“子类”或者“派生类”,反之,被继承的类我们称之为:“父类”或者“基类”,子类可以继承多个父类(派生类可以继承多个基类)但是一般情况下,一个子类只能有一个父类,要实现多重继承,可以通过多级继承来实现。继承的概念,笔者就先简单的介绍到这里吧,如果还是有些不明白的读者,可以在网上找些资料,相信博客园里面也有对继承概念有专门讲解的博文。

    在我们C#或者JAVA中,不管我们自己创建、调用任何一个类,它的顶级父类只有System.Object这一个类。也就是说,所有的类都是通过它进行衍生的。在我们创建完一个类的时候,类里面任何方法、字段、属性都不去写,在另一个类中进行实例化之后,都会看到一些方法如:Equals();GetHashCode();ToString();GetType()等。那么这些方法就是System.Object类中的方法。这就是一个继承的概念,在我们创建一个类的时候,它继承了System.Object类、拥有了System.Object类中的方法,并且对System.Object类进行了一个扩展。上面我提到一个实例化。我们都是用new关键字来对一个类进行实例化(非静态类(static))在我们new了一个实例化的时候,会自动在堆上为我们创建一个“类型对象指针”和“同步索引快”,而且CLR会自动给我们分配一个该类所需要的内存,这个内存的大小根据类中的字段所需要的字节数进行设置,然而,最终new关键字会调用System.Object的构造函数,并进行返回,会返回给新对象一个引用(对象指针),我们所有调用类中的方法,属性的时候,是根据这个引用的内存地址进行操作的。也可以理解为我们操作内存(实际上是CLR帮我们完成的),到这里可能会有一个问题,就是,这个对象的内存我们 如何去释放。这就轮到我们CLR的垃圾回收机制(GC)进行检测,并对不再访问的内存进行自动释放操作。垃圾回收机制将在基本内容更新过以后进行更新。

   在我们日常的使用中,会非常频繁用到类型的转换。那么类型是可以随便进行转换的么(比如Person类型转换成DateTime类型)?答案是不可以。可是为什么不可以?首先,CLR最重要的特性之一就是安全性。这个安全性就已经把我们限制死了。在我们使用反射的时候会用到GetType()方法,这个方法是一个非虚方法,是不可重写的方法,这个方法不可重写,就意味着我们不能把Person类通过GetType方法伪装成DateTime类型,并进行返回。在类型转换的时候,CLR准许我们将一个父类强制转换成它的子类,而不需要任何特殊的语法把子类转换成父类,例如:

namespace Chapter4
{
class Program
{
static void Main(string[] args)
{
//子类转换成父类不需要强制转换
Object o = new Person();
//父类转换成子类必须进行强制类型转换
Person p = (Person)o;

}
}

//该类继承自System.Object类
internal class Person
{
private string name;
private int age;
}
}

那么两个类都集成自Object类,为什么就不能相互进行转换?CLR在每次进行转换的时候,都会对转换的对象进行检查,确定o是否引用自Peron对象,或者是否是从Peron类派生 的任何子类,如果是CLR将准许转换,否则将抛出异常,就拿刚才说的Peron转换成DateTime为例,代码如下:

 class Program
{
static void Main(string[] args)
{
//子类转换成父类不需要强制转换
Object o = new Person();
//父类转换成子类必须进行强制类型转换
Person p = (Person)o;
//将Person的子类转换成Person
Man m = new Man();
MagicPerson(m);
//构建一个DateTime对象,并转换成Person
DateTime dtNow = DateTime.Now;
MagicPerson(dtNow);

}

private static void MagicPerson(object o)
{
Person p = (Person)o;
}
}

//该类继承自System.Object类
internal class Person
{
private string name;
private int age;
}
//该类继承自Person(最高基类为System.Object)
internal class Man : Person
{
private string sex;
}

以上的代码完全可以通过编译,并运行。那么第二次运行到MagicPerson方法的时候,CLR会给我们抛除一个为:System.InvalidCastExiception异常,并告诉我们转换失败。为什么会转换失败?我们来分析一下CLR进行类型转换时候的工作步骤。首先在编译时,编译器并不知道MagicPerson中参数o到底是谁的引用,所以编译器准许代码编译通过。但在运行时,CLR知道了第一次进来的o是Man的引用,并继承自Person类,第一次转换成功。当再次进入MagicPerson方法的时候对象的引用为DateTime的引用。CLR会核实DateTime类型是否继承自Person或者是否是Peron类型,发现并不继承自Person类也是Person类型,便抛出异常信息终止转型。
     在以上的代码例子中,MagicPerson方法在实际运用中参数应该设定为Person类型,这样的话,编译器就会报错,在造成更大损失之前,把罪恶的种子掐死在摇篮之中。

     先去吃饭,吃饭回来之后接着更新。

   

posted @ 2011-12-05 13:18 LouisLee 阅读(1210) 评论(12) 编辑

2011年9月15日 #

摘要: 本章将对CLR中的IL、CTS和CLS进行一个大概的总结。下章将开始第二章的内容。IL我们称之为高级汇编语言或者中间语言,在我们编写基于.NET框架下的程序(不论是C#、VB或者J#等)在被编译成本地CPU指令前,都将被编译成IL语言,再有CLR对IL语言进行编译和控制。比如用VB在控制台应用程序写入如下代码: Sub Main() Console.WriteLine("VB")End Sub再用C#在控制台应用程序写入如下代码:static void Main(string[] args){ Console.WriteLine("Test");}虽然语阅读全文
posted @ 2011-09-15 13:54 LouisLee 阅读(1057) 评论(4) 编辑

2011年9月14日 #

摘要: 在文章的开头,我要感谢博客园的各位看官对小弟的上一篇文章的支持。就像有网友说的一样,这本书的前三章确实比较难懂,需要用心和反复的去读并揣摩(当然,不要去痴迷)在进行下一章内容前(由于笔者水平有限,还没有完全理解,故本章不会进行讲述),我想先说个基本概念,什么是堆栈?(有点新瓶装老酒的感觉,但是理解堆栈真的很重要)首先,我们要非常的清楚堆和栈是两个不同的概念(尽管我们经常把堆栈一起说)在C#中,我们把引用类型放在堆中,把值类型放在栈中(如果对值类型和引用类型不是很明白,请百度或者google,笔者就不在这里做解释了,望海涵)堆和栈的区别在于,堆是由系统自动分配的,而且系统不去释放,需要我们自己手阅读全文
posted @ 2011-09-14 12:19 LouisLee 阅读(1196) 评论(5) 编辑

2011年9月13日 #

摘要: 相信很多有过面试经历的开发人员都在面试的时候被问及到.net比较底层的东西,如程序在底层是怎么运行的?笔者就遇到过面试官问反射的机制是什么?当时确实答的一塌糊涂,但是有幸还是找到了一份不错的工作。在工作中猛然觉醒,是时候(有一定的开发基础)有必要了解一些.net运行的机制了,在买了一本CLR Via C#的书并在细细品味了第一章之后,决定把所学到的东西与大家进行分享,虽然之前都知道的知识,但是还是让我学到了很多的东西。可能总结的过程中还是有些不对的地方,希望各位看官能指出,并提出,我将尽快改正。谢谢!(目前只总结第一章前半部分,后半部分文档还未整理出来,望大家海涵) 言归正传,先从CLR是什.阅读全文
posted @ 2011-09-13 13:08 LouisLee 阅读(1911) 评论(16) 编辑

2011年8月31日 #

摘要: 安装SQL Server 2000 提示文件挂起错误解决办法 安装SQL SERVER 2000时提示: “以前的某个程序安装已在安装计算机上创建挂起的文件操作。运行安装程序之前必须重新启动计算机。” 接着按照提示重启计算机,再安装,仍然出现同样的提示。原因是安装程序在先前的安装过程中在系统注册表留下某些信息,导致不能安装。删除掉如下键值信息即可安装: 在运行窗口输入regedit,打开注册表编辑器,在HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager中找到PendingFileRenameOperations...阅读全文
posted @ 2011-08-31 09:42 LouisLee 阅读(41) 评论(0) 编辑

2011年5月16日 #

摘要: (本文章转自风尘浪子博客园中的一篇博文,原文连接地址:http://www.cnblogs.com/leslies2/archive/2011/05/11/2040996.html)记得在下第一次接触.NET Remoting分布式开发是在2003年,那时候是Framework1.0初次亮相之时,Remoting分布式开发是Framework1.0其中一个亮点。经过多年的发展,在2005年,WCF随着Framework2.0首先亮相。WCF是结合Remoting,Web服务,TCP/IP套接字,MSMQ信息,P2P,WSE等多方通讯的混合体。随着WCF的出现是否意昧着Remoting即将没落,阅读全文
posted @ 2011-05-16 14:54 LouisLee 阅读(40) 评论(0) 编辑

仅列出标题  下一页