以下总结了C#与C++的差异,不一定全,主要目的是供准备从C++转到C#的程序员参考。其中一般仅仅叙述了C#各方面的特性,C++的特性在下面所列的每一个方面都跟C#不同,但不再赘述。
(1) C#中无指针,所有的对象被分成了值类型和引用类型两种,前者包括int, double, char等简单类型和struct,后者包括string, 数组和类。值类型在栈上分配,即使它是用new分配的;引用类型在堆上分配,即使它没有使用new分配。引用类型仅仅在栈上保留一个对象的handle,对象本身由GC(垃圾回收器)统一管理和回收。
(2) C#中传递参数若要传引用则用ref或者out。它们的区别是out参数在传入前不必赋初值,但是函数退出前必须赋值给它。
(3) C#中的struct:<1> 它是值类型,当在函数中用MyStruct ms = new MyStruct(),它是在栈上分配内存,所以该内存在离开函数时会被释放。<2> struct有缺省的构造函数,它会把每个数据成员置0,不能明确定义缺省构造函数,但是可以另外定义其他带参数的构造函数。<3>struct的数据成员声明不得包含初始化动作(C#的类可以)。<4>不能为struct提供析构函数。<5>对MyVar [] vars=new MyVar[1000], 若MyVar是一个class,则上式产生内含1000个null handle的数组,并额外调用1000次operator new,为1000个对象在受控堆上分配空间;而若MyVar是一个struct,则它将建立含有1000个MyVar Object的数组。<6>struct不支持继承等一系列机制。
(4) 数组在堆上分配,访问数组成员可能比C++效率更低,因为C#会对每一次访问进行检查。除非为array中每个元素指定初值,否则每个元素都被初始化为缺省值(任何数值型的缺省值都是0,ref类型的缺省值都是null)。
(5) ArrayList为动态数组,存储于连续内存中,一旦内存被填满,它就分配一块更大的连续内存(通常是原来的两倍)并把既有元素复制到新空间。
(6) Class的所有数据成员都会自动初始化为其类型的缺省值:int和double 为0, bool 为false,ref为null,这些是在new时完成的。也可以在声明中明确指定初值,如
Class Login
{
Private int myint=20;
Private ArrayLit mylist=new ArrayList();
Private string [] m_str=new string[] {“sfs”, “sdfsf”, “ggg” };
}
(7) C#不支持函数指针,但委托可以实现对应的功能。如果为delegate object加入nonstatic成员函数,那么该函数地址及籍以调用该函数的那个object(即调用类成员函数时需传入的this指针)的handle都会被存起来,造成的结果是该Object的引用计数加1。
(8) C#中提供了interface,它与abstract class的区别是它不能定义缺省的实现或任何数据成员。它支持多重继承(而class只支持单一继承)。另外一个interface所具有的含义与abstract class也不同,前者侧重描述某一些类都具有某种功能,后者侧重描述某些类都属于某类事物。
(9) C#中没有全局变量,所有的方法、变量全部定义在类中。
(10) C#中的数组实际上是一个派生自Array的类的对象的引用。
(11) C#中多了个decimal数据类型,还多了byte类型表示一个字节,字符类型char来表示UNICODE的字符,代表两个字节了,long 数据类型为 64 位,而在 C++ 中为 32 位。
byte 1 无符号字节型 sbyte 1 有符号字节型
short 2 有符号短字节型 ushort 2 无符号短字节型
int 4 有符号整型 uint 4 无符号整型
long 8 有符号长整型 ulong 8 无符号长整型
float 4 浮点数 double 8 双精度数
decimal 8 固定精度数 string unicode字串型
char unicode字符型 bool 真假布尔型
(12) C#中所有的类和各种值类型都继承自Object类,一个Object转换为ref类别,只需要把目标的handle设为Object的handle,而把一个Object转换为值类型,需要做更多的工作,因为value Object直接内含其数据。值类型和Object的相互转化称为装箱/拆箱。
(13) 若要把整数表达式赋予enum,必须强制转换,但是这样会忽略对此值的一切类型检查。缺省情况下,每个枚举值以int来表现,我们也可以指定其他整数类型作为枚举元载体,前提是我们指定的类型能表现枚举元的所有值,如enum weekdays:byte {Sunday, Monday, …};
(14) C#中的switch语句: 可以用字符串string作为判断条件,且每个case语句必须带有break, return, throw, goto等终结语句,但是空的case语句不用带break。
(15) 整数运算可在checked 或unchecked环境中进行,前者若运算结果超出目标类型储值范围会抛出OverflowException,unchecked包含的表达式忽略溢出检查。比如可以用byte myvar = checked ((byte)sourcevar),此时转换不正确程序会崩溃。另外浮点运算永远不会抛出异常,“除以0”只会导致正负INF,其他无效运算可能导致NAN(not a number)。
(16) C#中叫成员变量叫做域;C#提供了一个foreach语句可以用来枚举可枚举集合;C#中提供了属性(property)这种操作成员变量的方式。
(17) C#中必须用abstract关键字声明抽象类,C++只要包含纯虚函数即可。声明重写方法必须使用 override 关键字,否则即使父类跟子类的方法有相同的signature而且在父类中是virtual方法,也被认为是“hide”而不是“override”。
(18) C#中提供了密封类(sealed)防止类被继承,主要作用是可提高效率。
(19) C# 中没有头文件和 #include 指令。
(20) C#中可以在关键字之前加上@而使用它作为普通的标志符。
(21) C#中有readonly关键字,它声明的成员与const成员的区别在于初始化的时机不同。前者在运行期,后者在编译期。所以不能有const myvar= new MyClass,而可以用readonly这样声明。唯一一个可以声明为const的ref是string,即可以这样声明:const string mystr=”sdfasf”;另外readonly 成员是instance member,而const成员则一定是static member。
(22) C#中可用来重载的运算符比C++要少。 另外,一个操作符如”*”被重载,它的“compound assignment operator”如“*=”会被自动实现。被重载操作符参数不可以是ref或out。我们也不能改变操作符原始定义的参数个数,我们也不能改变操作符的优先级。 . () new 这三个操作符不允许被重载。
(23) C#中,程序可以在运行期获得已编译的装配件(库和可执行文件)中的类、方法、属性等定义的信息。
(24) 可变长参数:void MyFunc(string s, int I, params int[] args);一个函数只能有一个params array且必须在最末尾。它只能是一维数组,且不能用ref或out修饰。
(25) Runtime type identification:
Shape a = new Shape();
if (a is Circle) {
Circle p = (Circle)a;
Console.WriteLine("a is circle");
}
else {
Console.WriteLine("a is not circle");
}
Circle q = a as Circle;
if (q != null) {
Console.WriteLine("a is circle");
}
else
{
Console.WriteLine("a is not circle");
}
if(a.GetType()==Circle.Type())...
if(myObj.GetType()==typeof(MYOBJ)) {…}
string str=(string)obj;这样的显式转换如果类型不对会抛出异常,所以最好用上述方法检查先。
(26) C#中调用函数可以用myFunc(val)或者myFunc(ref val),它们分别对应myFunc(int val)和myFunc(ref int val)。
(27) internal的class只能由本项目调用,public的class可以由其他项目使用。
(28) 可以在派生类中用new声明一个方法来隐藏基类的方法。此时若用一个基类的指针调用该方法是调用基类的。
(29) 可以用partial声明一个类,表示在此仅仅定义了该类的一部分。
(30) struct A; A a1 = new A(); A a2=a1;//Here is a new obj and a shallow copy.
class A; A a1 = new A(); A a2=a1;//a2 simply refers to the same obj.
(31) In C# struct members will be reordered automatically by default, so there is no packing problem. This corresponds to StructLayoutAttribute.LayoutKind.Auto. If we use StructLayoutAttribute.LayoutKind.Sequential, it will not be autimatically reordered, but there is still packing problem, so this is same with C++. If we use StructLayoutAttribute.LayoutKind.Explicit, we can control it.
(32) C# 支持static contructor,该构造函数有且仅有一个,为public且不允许更改,它在此类的某个实例被创建或类的某个static成员被取用时才被调用,且仅被调用一次。
(33) In C# inline functions are not controlled by programmer, it is controlled by .net framework.
(34) Once string is defined, it cannot be changed any more. C# modifies the string by returning a new string. So if string is just for presentation; if we want to use modifiable string, use StringBuilder.
(35) The keyword ref can only be used for passing parameters, for value objects, we can not just store their handles in array. So if we store some data structures in an Object array, we'd better make the elements class instead of struct, otherwise the boxing/unboxing will make efficiency low.
(36) C#中所有"ksfkjklf"这样的字串全部是UNICODE,不需要用L, _T等宏声明。string有提供的方法把成员转化为char[],这也是一个16位的数组,要想转化为ASCII,可以自己定义映射或者使用System.Text中的方法:AscEncoding等。
(37) String name = @”C:/fic/1.txt”其中的@表示其后的字符串是”verbatim string”,这种string其中的’/’不用写成’//’,而且这种字符串能够跨越数行,其中换行,TAB等字符都保留,这样允许生成带有格式的文本块。
(38) C#中提供了一个lock方法进行线程同步,它可以看作EnterCriticalSection /LeaveCriticalSection的替代品,但是用法又不完全一样。
(39) C#中把事件处理机制作了更好的封装,一个事件其实就是一种委托,它跟普通委托的区别在于一方面它有标准的参数和返回值类型:public delegate void MyEventHandler(object source, MyEventArgs args);另一方面它不是用new声明一个实例,而是直接用:public event MyEventHandler myhandler;这样的形式声明。
(40) C#中也可以使用使用C++代码,但必须用unsafe声明这段代码。代码中可以用fixed来使得一个指针固定不变,还可以用stackalloc来在栈上分配一个C++中那样的数组。
(41) C#内存连续,分配快,回收慢;C++长期运行内存碎片可能多(而且无法克服,因为搬移会造成指针变化),分配慢,回收快。所以C#适合对速度要求不是特别高,用户界面程序或者长期运行的服务器程序。C++适合苛求速度的场合。另外,C#的相关内存块往往相邻,而C++的相关内存经常相邻很远。C#的相关内存块更容易出现在同一页面上,从这方面来说效率更高。
总结:
C#是一种我们在用C++的时候经常想象的语言,比如我们用C++的时候可能经常想“如果我不用花那么多时间考虑内存泄漏多好”,“如果我能运行中判断对象类型多好”。它一方面提供了我们这些梦寐以求的特性,另一方面从各个细节上比C++更加规范和明晰,比如抽象类的声明,比如struct和class的分工。加上强大的.net类库支持,相信它能使得我们在写代码的时候烦心事更少,从而提供我们更高的开发效率。所以个人认为,除非特殊要求的场合,比如不能引入.net平台支持,比如对运行速度有特殊要求,比如需要直接操纵内存的底层开发,其他场合用C#会更好一些。