using System; namespace ConApp //命名空间 //C++中的#include语句包括了在编译时的资源代码,而C#中的using语句的作用仅仅是告诉编译器在哪个命名空间中 //找到没有被它们的完全限定名称引用的类型,或者是不在默认状态或全局命名空间中的类型。 //C#是完全面向对象的语言,意味着每样东西(所有功能和数据项)都必须是类的一部分,除using语句外。 //在C++中,Main()是一个全局函数,但在C#中Main()是类的一部分。这里的helloWorld之所以存在就是为了 //保存Main()方法。Main()是helloWorld类的一个静态成员,表示属于类而不属于任何一个实例 |
注释
//或/**/用于注释文本,而///是文档注释,用于从C#代码中生成XML文档
数据类型
*引用型
包括类、接口和数组
引用型数据的特性:
1.包括了类中的所有东西,例如,方法、属性、构造函数等等
2.不管引用型是如何被实例化的,总是在托管堆上进行创建,如Foo f=new Foo(),这里创建了一个Foo对象时,您所获得的就是对Foo的一个引用。C#中的字符串是引用型的,但是并不都是使用new操作符来创建的
3.访问引用型数据时需要使用引用!
4.传递拷贝、值和参数都要使用“引用”!
5.无用单元收集自动对引用数据类型进行处理(接口实际上不占用内存,所以不被收集)
*值型
C#中内置的类型几乎都是值型,是以位的正常次序存储在程序堆栈上,具有更高效率。
创建值型数据不需要使用new关键字,而是使用标准的本地变量声明的形式来完成。如
//Creating a value type
Point p;
可以使用struct或enum关键字创建新的数据类型。
如果结构使用了构造函数,可以使用new操作符来实例化一个结构,并以特定的值对其成员进行初始化。
值类型数据的特性:
1.值类型不能从引用类型中继承,而且不能作为超类来使用。
2.值型数据是在对堆栈中创建的,没有涉及到分配和释放单独内存块的操作,所以创建和破坏操作具有较高的效率。
3.可以被直接访问,访问效率很高。(因为使用时不是引用)
4.默认情况下,值型数据用值来拷贝和传递的,并非用引用来完成。
5.不被无用单元回收器评价和收集。
装箱与拆箱
为什么可以装箱?因为C#中所有的类型根本上都是从Object类中继承而来,而Object类位于继承树上根部,因此定义的用来保存类型Object引用的通用容器,理所当然的可以保存任意的数据类型。
C#提供了一个根据需要将类型转换到对象中以及从对象中转换出来的方法,这意味着值类型具有了基本的位模式的高效率,而且还拥有了可以使用的对象。
当一个值型数据被装箱时,系统其实是将其转换为一个对象:分配了一个对象实例,而且值型数据的值被拷贝到了箱中
拆箱操作有且只能使已被装箱对象再转换回一个值类型,该操作是使用强制类型转换语法。如
//Box a value int i=123; object o=i; |
![](https://i-blog.csdnimg.cn/blog_migrate/819fa2a923ebf1a271f55b49610edda2.png)
//Unbox it int j=(int)o; |
该强制类型转换实际上表示“检查装箱的对象中的类型是否位int,如果是的话则提取该值并将它拷贝到j中”,否则将抛出一个InvalidCastException异常
C#内置的数据类型
除了string和object是引用类型外,其他的内置数据类型都是数值型的。有些并不符合通用语言规范中规定。
其中void类型表示一个方法的‘返回类型’,该类型表示什么都不返回,而且不能将void作为一个参数来使用。
字符型
C#包含了基本的char值类型和string引用类型,都使用16位unicode字符,是得有足够大的空间来存放各种国家字符集。
常量
常量不能更改,使用const关键字声明,但必须对其初始化。
const int m = 3; //必须初始化 const int n = m * 4; //能够被编译器计算出来的表达式 |
而以下的则是错误的
int p = 3; const int q=p+4; |
运算符(略)
创建和初始化变量
用户能够直接定义值型数据并对其进行初始化。如 float f=3.13F;
引用类型数据要用两部分进行创建:引用变量和对象本身。如
//Creat a Person对象,假定Person为引用型数据
Person p1 = new Person();
new运算符在托管堆上创建了一个新的Person对象,并返回了对这个对象的引用,而且将它赋给了p1。变量p1是一个引用变量,该变量指向了Person对象。
如以下代码所示,只是将引用变量的值拷贝给另一个引用变量,而不是将前一个引用变量所指向的对象拷贝给另一个引用变量。
//Creat a new string and store the reference string s1="abc"; string s2; s2=s1;// s2 and s1 refer to the same object |
变量比较
正如上段所述,在对引用数据进行比较时需要小心,因为默认的情况下==和!=运算符比较的是引用的值,而不是对象的内容。(当然,我们可以重载运算符来比较引用的内容),如
using System; namespace ConApp if (n1 == n2) public Name(string s) |
这个控制台应用程序用Ctrl+F5执行後的输出结果为This should not happen!。虽然我们创建了两个包含同样数值的实例,但是对两个实例进行比较时失败,因为n1和n2这两个引用指向了两个不同的对象,它们不能“相等”。
如果想比较对象的内容,就需要使用Object类的四个通用方法Equals()方法。
在string类型中,为了实现对字符串变量中内容的比较,需要重载==和!=运算符,如
using System; namespace ConApp if (s1 == s2) } |
Ctrl+F5後输出结果为Strings are equal!这里重载了吗?有点糊涂了
字符串构造
不使用new运算符而从字面值构造一个字符串。如,string str1="http://www.ebailu.com.cn";
但从“性能原因”考虑不应该使用从字面值构造一个字符串的构造函数?
数组
C#支持三种类型的数组:一维、矩形和参差。(第六章将有详细介绍)
声明一个一维数组,如
int[] array1=new int[10]; //这是一个值型数组
C#中的数组基于0的索引。
声明并同时初始化一维数组:
int array1={1,2,3,4}; //常用方式 或 int[] array1=new int[]{1,2,3,4}; |
如果创建一个数组对象,但想在以后再对其进行初始化,则必须使用new语法,如下
//This is correct int[] array1; array1=new int[]{1,2,3,4};
//But this gives a compiler error int[] array1; array1={1,2,3,4}; |
选择和循环控制语句
1.goto语句
该语句的功能是在switch语句中的情况之间进行跳转,但在一个完全结构化的面向对象语言中不应使用该语句。
2.if语句
if语句中的表达式的计算机过必须是bool值。
if(expression1) {……} else if(expression2) {……} else …… |
3.for语句(循环)
for(initializer;condition;iterator) { statement; }
//如下面的语句是死循环endless loop for(;;) {……} |
4.中断和继续关键字
break和continue用于控制迭代循环
break语句可以立即从循环语句中退出
continue语句将终止当前的循环,并跳出到组成循环体的程序块结尾
C#不支持带有标记的中断,因为从嵌套循环中跳出来会使程序变得杂乱??
5.do……while语句和while语句(循环)
与while语句不通,do……while循环主体至少执行一次。如
6.foreach语句(循环)
该语句可以遍历数组和集合中的元素,与VB中For Each结构相似。
该语句可以很好的与System.Collections命名空间所提供的几个集合一起使用,如List和HashTable,抑或是实现Ienumerable的任意对象。(详见第六章)
举例:
int[] array1={1,2,3}; foreach (int i in array1) { Console.WriteLine("Value is {0}",i); } |
7.switch语句
switch表达式必须是常量,可能是整型(包括char型)或者string类型的。
如果要从一个Case“贯穿”到下一个Case,只是简单的省略break语句是不够的,必须使用“goto case 'Name'”。
定义和调用方法
[modifiers] return-type meth-name ([parameter-list]) //可以有N个修饰符(modifiers) //必须有返回类型,包括void类型(return-type) //可以不带参数(parameter-list) |
参数如何传递给方法呢?
*如果方法参数是值型的,则传递给方法的是参数的拷贝,方法中对本地拷贝的修改不会影响到原始参数的值。
*如果参数是引用类型的,则所传递的是对象的一个引用,在方法中的任何修改都将影响到对象本身--没有涉及到拷贝。
如何使用ref和out关键字?
*用ref关键字来标记一个参数就意味这对它的值所做的任何修改都会保存下来,就像一个引用型返回值一样。如
void refMethod(ref int n) { n+=3; } …… int p=3; //ref参数必须初始化,否则编译器报错。 refMethod(ref p); //vallue of p will be changed //这里的ref关键字告诉编译器以引用型来传递返回值而不是以值型来传递。 |
* 用out关键字则使方法给参数赋一个值,而不是修改它的现有值。象ref一样,out意味着方法中的参数引用指向了调用代码中的原始变量。不同之处在于,ref参数必须在调用方法前被初始化,而out参数则不必初始化就可使用。
方法的重载
方法名相同,而参数数量或类型不同。
但是返回值类型不同的方法不是重载。
错误处理
在C#中无法抛出所有类型的实例,抛出值必须是System.Exception对象的一个引用(或是由一个派生类生成的对象)
*处理异常应该使用try,catch和finally程序块。
注意:用户必须正确排列catch程序块,如果后一个catch程序块没有被执行,系统将抛出一个编译错误。
而捕获任何异常的catch程序块称为常规捕获子句,不应经常使用,因为它没有指定一个异常变量。如
try { //Code which may fail } catch { //handler error,遗失了异常带来的信息,所以无法知道到底发生什么错误。 } |
*finally程序块
如果finally程序块与try一起使用,那么无论try程序块是如何被执行或抛弃的,finally程序块都将执行。
如果finally程序块的过程给发生了异常将怎么办呢??
*异常类:均由System.Exception类派生
*选中与取消:关键字checked和unchecked可以用来为算数运算和整型数据转换控制溢出检查。
着两个关键字的特殊之处在于,既起着运算符的作用又起着语句的作用。
输入/输出(I/O)控制
System.Console类提供了控制台的I/O
Console.In() //访问标准的输入流 Console.Out() //访问标准的输出流 Console.Error()//访问标准的错误流 |
*输入控制
Console.Read() //以int类型返回一个单独字符,否则返回-1 Console.ReadLine() //回一个包含下一行输入结果的字符串,否则返回null值。可以使用字符串类型的Length属 |
*输出控制
Console.Write() //输出的值不带换行符 Console.WriteLine() //出的值带换行符 |