Jacob的C++程序员光速入门C#系列:
C++程序员光速入门C#(一):总览、数据类型、运算符
C++程序员光速入门C#(二):流程控制、函数、类
C++程序员光速入门C#(三):继承、泛型、异常
之前看了一个很有价值的系列博客,博主以C++的视角来入门C#,个人感觉非常的棒。虽然是很久以前写的了,很多新特性都没有涉及,但作为入门参考学习还是很有价值的。这里我把它重新排版修订一下,作为读书笔记,也希望能帮助到读者。原博客地址: C++程序员快速学习C#---(一)
一.Hello world!
随着.NET的深入人心,作为一个程序员,当然不能在新技术面前停而止步,面对着c++在.net中的失败,虽然有一丝遗憾,但是我们应该认识到,对于c++其实就不应该对其在.Net中的表现有太大的奢望,因为毕竟它并不是一个.Net下的正统语言,.Net应该是c#的舞台,作为一个c++程序员,我们应该庆幸,因为我们学习c#其实是简单的直接的,需要我们接受的新知识其实不多。相对其他语言来说,我们应该可以更加平滑的过渡到c#的开发中.废话不多说,现在就让我们用c++的基础来学习这个渐渐壮大的新语言-----C#。
对于C#的讲解我只讲解和C++有区别的地方,相同的部分我就一带而过,这样的对比学习可以让我们在已有知识的前提下快速掌握C#。
一开始学习语言大部分的教程都会用一个Hello World程序来示范,我们也落入俗套,用Hello World来和C++中做一个比较。
/******************C++程序**********************/
#include <iostream>
using namespace std;
int main()
{
//C++程序
cout<<"Hello World!"<<endl;
return 0;
}
/********************C#程序*********************/
using System;
namespace HelloWorld
{
class Class1
{
//C#程序
static void Main()
{
Console.WriteLine ("Hello World!");
}
}
}
乍一眼看上去两者差不多,心中一阵窃喜,可以说C#对语法的定义更加严格一些。
首先对于程序的进入点,最大的区别就是Main函数的开头必须要大写。因为C#是一个完全面向对象的程序语言,所以它的所有代码都必须定义在一个类中,Main函数也不例外。同时因为.net程序在编译运行时都是先转为中间语言,然后中间语言再编译为机器语言,这样的好处有2个。一,如同Java一样,写好的程序可以在不同的系统中运行,而不需要改变程序;二,使用不同的语言写的程序,因为要转化为相同的中间语言,所以在程序开发中可以使用不同的程序语言编写,而相互调用。
当使用不同语言开发或者进行分类开发时,各自开发的程序中会出现相同的变量名,函数名等,所以在写C#程序时,必须把程序包涵在一个名字空间内。C++在多文件编程的时候出现重复的变量名的时候,会比较头疼,C#则没有这个问题,因为所有的类定义都要在一个命名空间里,而变量只能定义在类中,不存在所谓的全局变量。
定义名字空间使用关键字:namespace <空间名>,当一个命名空间中的代码需要使用在另一个名字空间中定义的名称,就必须包括对该命名空间的引用,使用点字符(.) 。
/********************C#程序*********************/
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
LevelOne.A a1 = new LevelOne.A();
a1.a = 10086;
LevelOne.LevelTwo.A a2 = new LevelOne.LevelTwo.A();
a2.a = 10086;
}
}
namespace LevelOne
{
class A
{
public int a;
}
namespace LevelTwo
{
class A
{
public int a;
}
}
}
}
这里就定义了两个名字相同的变量,我们可以使用LevelOne.A 和 LevelOne.LevelTwo.A 来独立创建两个对象,它们互不干扰。建立了名字空间后,我们可以使用using关键字来简化对它们包含的名称的访问。和C++中使用using namespace std的含义相似。
对于主函数必须要有限定词static 这表明Main函数是静态的,在内存中只能有一个副本。
第一行中的using System.其命名空间是.NET应用程序的根名字空间,包涵了控制台应用程序所需要的所以基本功能。就如同C++中的头文件包涵在std这个名字空间中一样。
Console.WriteLine ("Hello World!");
Console是system名字空间中的一个类,其有一个WriteLine方法,它的作用和cout一样,输出一行字符串。
二.数据类型
C#中的数据类型和C++是类似的。
1.浮点类型
浮点类型中添加了一个精度更高的decimal类型,对于金融方面的程序开发,此种数据类型用来定义钱币.
2.bool类型
bool类型的变量只能赋值为false和true。虽然它们的含义仍然是0和非0,但是在使用中不能再给它们赋值成整数值,在判断语句中 if (bool a==1) 的使用都是错误的。这样的语法让bool类型的意义更加准确。
3.字符类型
char类型在C#中是16位的,它不能接收一个整数值,这与C++有所不同。
4.整数类型
整数类新中添加了byte(8位无符号整数),sbyte(8位有符号整数),short(16位有符号整数) 类型
long变成了真正的64位有符号整数,它可以用在64位机器的编程中。
uint,ushort,ulong顾名思义是没有符号的整数,它类似于C++中的unsigned int,名字换了一下而已。
long类型在C++中的大小是根据编译器位数改变的,32位编译器的时候是4字节,跟int没区别。而64位的时候才是8字节,坑爹!
5.字符串类型
string类型是字符串类型,它是引用的类型,它的使用方法和C++中string的使用相似,可以进行+运算(运算符重载)。
string类型有一些方法可以给我们使用,类似C++。例如:ToCharArray() 把字符串放入一个字符数组中等等,可以在MSDN中查找。
6.类型的转换
C#是一个强类型的语言,它的数值类型有一些可以进行隐式转换,其他的必须显式转换,隐式转换的类型只能是长度短的类型转换成长的类型,例如int可以转换成long,float,double,decimal。反之必须显式的转换。
int a=7;
float b=a; //隐式转换
long c=12L; //和C++一样必须加上后缀L才能将一个常量定义为long型
a=(int)c; //显式转换
使用上面的显示转换不能用在bool和string类型上,如果希望string或者bool类型和整数类型之间的转化可以使用一个方法:
//Convert.To*****(val)
//*****:一种数据类型(具体请参看MSDN) val:可以是这种类型的变量
int a=123;
string str=Convert.ToString(a);
bool m_bool=Convert.ToBoolean(a);
7.枚举类型
C++和C#的枚举类型,定义相同,使用也相同,只要注意C#中语句最后不需要;结束符。同时定义枚举类型时也不许放在主函数代码段中它只能放在执行代码外面,如下
using System;
namespace HelloWorld
{
class Program
{
enum Week
{ monday=5, tuesday, wednesday,thursday, friday, saturday,sunday }
static void Main(string[] args)
{
int a = (int)Week.tuesday;
//输出数字6,Right?
Console.WriteLine(a);
Console.ReadKey();
}
}
}
8.指针类型
c++中奉为经典的指针类型,在C#中已经取消了,真不知道这个是一个好消息还是坏消息,不过在易用性方面来说因该是一个进步。不过c#中其实在隐藏了一个指针,我们会在后面说到,同时在C#中也可以包含不安全代码,这些代码就是使用了指针的代码。
9.结构类型
C#中的结构类型看上去和C++没有什么区别,定义使用也相似,但还有有很大的区别的,首先就和枚举类型相似, 最后不需要;结束符,同时定义时也不许放在主函数代码段中它只能放在执行代码外面。其二最大的区别就是C#中的结构已经和类相似了,不同的地方在于:C++中的结构中成员变量是公有的,而C#中是私有的,C#中的结构和类的区别唯一就是不能继承(但是可以有接口,这个以后会讲到),但是C#结构是在堆栈中创建的空间,所以最好是对小量的数据进行操作。
/**********************C#程序************************/
using System;
namespace HelloWorld
{
class Program
{
public struct Student
{
public int intVar;
public double doubVar;
}
static void Main(string[] args)
{
Student a, b;
a.intVar = 1;
a.doubVar = 1.1;
b = a;
a.doubVar = 2.6;
Console.WriteLine("{0} {1} {2} {3}", b.doubVar, b.intVar, a.intVar, a.doubVar);
Console.WriteLine("{1} {0} {3} {2}", b.doubVar, b.intVar, a.intVar, a.doubVar);
}
}
}
/*********************************
输出结果:
1.1 1 1 2.6
1 1.1 2.6 1
**********************************/
/*******************************
//C#中的结构类型不能继承!
struct A
{ }
//ERROR!!
struct B:A
{
}
class C
{ }
//YES!
class D : C
{
}
********************************/
C#中的输出定位格式和C语言中的printf类似,但更加简洁,不需要在对不同类型的变量使用不同的占位符,只需对应后面跟着的变量,给出序号就可以了。
10.数组类型
数组的定义和C++有区别,看上去很别扭,定义语法为:
<类型>[] <变量名> 例: int[] num;
这样就定义了一个int类型的数组,但是切记它可和C++不同,[]里面可不要写内容哦!int[10] num可是错误的。确定数组的大小有两个办法:
一,在定义时指定数据
int[] num = {5,3,7,3};
二,使用关键字new,例:
int[] num = new int[4];
当然两者也可以合起来,例:
int[] num = new int[4]{5,3,7,3};
注意:前面定义了4个数据,后面花括号里面就必须有4个数据,不然就是错误的。
//错误!!
//int [] num = new int[4]{4,3};
并且,对多维数组的定义和C++是不同的。C++中定义为
int num[3][4] = {1,2,3,4,5,3,2,3,4,2,3,4};
C#中定义为
int[,] num = new int[3,4]{{1,2,3,4},{5,3,2,3},{4,2,3,4}};
注意,和C++不同C#不能在数据列表中不分类,也就是说不使用{}把一组括起来是错误的,而在C++中是正确的。
//ERROR to C#!!!
//int[,] num=new int[3,4]{1,2,3,4,5,3,2,3,4,2,3,4};
对数据的使用和赋值也相应的变为
//C#的数组成员使用和赋值
num[2,1]=3;
//C++的数组成员使用和赋值
num[2][1]=3;
C#在数组中最富革命性的改变,应该就是是添加了锯齿形数组(或者说交错数组)。例如它可以添加一组{{1,2,3,4},{2,3},{2,3,1}}长度不一样的数据,,在C++中只能创建一个三行四列的数组,在C#中它能够产生这样一个锯齿形数组,第一组中有4个数据,第二组中有2个数组,第三组中有3个,这样可以不浪费内存。
锯齿数组的定义和前面的定义也有区别,它更像是一个数组中包含了一个数组
int[][] num = new int[3][];
num[0] = new int[4]{1,2,3,4};
num[1] = new int[2]{2,3};
num[2] = new int[3]{2,3,1};
下面是上面的定义的一种简洁写法:
int[][] sum = {new int[]{1,2,3,4}, new int[]{2,3}, new int[]{2,3,1}};
三.变量
变量的使用和作用域和C++类似,没有什么特别需要注意的。但是首先我们应该看到,C#是一门完全面向对象的语言,也就是说定义的变量都变成了类的私有成员(定义时如果没加访问修饰符的话)。如果要在别的类中使用变量就需要在定义语句前加上访问修饰符public。
在C#中必须给每个变量添加访问修饰符
public int a;
public int b;
C#中的访问修饰符还有一些,如下:
internal:变量只能在当前程序中使用.
new:在用作修饰符时,new 关键字可以显式隐藏从基类继承的成员。
private:私有的,和C++中含义一样
protected:保护类型,和C++中含义一样
static:静态的,和C++中含义一样
readonly: 只读,在变量初始化(构造函数)以后就不许改变.
protected internal:双重限定,但只有这一个组合
访问修饰符new的用法如下
public class Program : BaseClass
{
new public class Test//2、new修饰符 显式隐藏从基类继承的成员
{
public int x = 2;
public int y = 20;
public int z = 40;
}
static void Main(string[] args)
{
var c1 = new Test();//1、new操作符 创建对象和调用构造函数
var c2 = new BaseClass.Test();
Console.WriteLine(c1.x);//2
Console.WriteLine(c2.y);//10
Console.ReadKey();
}
}
public class BaseClass
{
public class Test
{
public int x = 0;
public int y = 10;
}
}
四.常量
C#有两种常量,一种是const修饰的,一种是readonly修饰的。
const和readonly的区别是:
1.readonly为运行时常量,程序运行时进行赋值,赋值完成后便无法更改,因此也有人称其为只读变量。const为编译时常量,程序编译时将对常量值进行解析,并将所有常量引用替换为相应值。
2.readonly常量只能声明为类字段(即C++中的成员变量),支持实例类型或静态类型,可以在声明的同时初始化或者在构造函数中进行初始化,初始化完成后便无法更改。const常量除了可以声明为类字段之外,还可以声明为方法中的局部常量,默认为静态类型(无需用static修饰,否则将导致编译错误),但必须在声明的同时完成初始化。
static void Main(string[] args)
{
//OK
const int a = 10;
//ERROR!!
//readonly int b = 10;
}
3.由于const常量在编译时将被替换为字面量,使得其取值类型受到了一定限制。const常量只能被赋予数字(整数、浮点数)、字符串以及枚举类型。而readonly则可以修饰类对象。
//ERROR!!
//public const DateTime D = DateTime.MinValue;
//OK
public readonly DateTime D = DateTime.MinValue;
五.运算符
C#中的运算符,优先级和C++一样,但是需要注意下面的四个运算符
* , -> , & ,sizeof
上面的四个运算符在C#的不安全代码中可以使用,但在一般的C#代码中使用是错误的,C#取消了指针,当然和指针有关的操作符都不能用了。
Reference:
C++程序员快速学习C#---(一)
C#高级编程 ——Christian Nagel & Jay Glynn & Morgan Skinner