0. 环境准备

0.1 简介

C#又称”C Sharp“,是微软发布和维护的一个现代的、通用的面向对象的编程语言,是专为公共语言基础结构(CLI,由可执行代码合运行时环境组成,允许在不同的计算机平台和体系结构上使用各种高级语言)设计的。

C#的优点:

  • 现代的、通用的编程语言
  • 面向对象
  • 类型安全
  • 容易学习
  • 面向组件
  • 结构化语言
  • 产生高效率的程序
  • 可以在多种计算机平台上编译
  • .Net框架的一部分

C#运行在.Net Framework上,可以用于开发不同类型的应用程序:

  • 桌面应用程序
  • 网络应用程序
  • 分布式应用程序
  • Web服务应用程序
  • 数据库应用程序
  • Unity3D游戏程序

.Net Framework是一个平台,它提供了一个跨语言的统一编程环境,而C#则是一种在.Net Framework平台上使用的编程语言。

0.2 推荐IDE

  • Visual Studio

  • Visual Studio Code

  • 在线IDE

0.3 基本语法

using System;// 这是单行注释
/*
这是多行注释
这是多行注释
*/
namespace Application
{class Rectangle {// 成员变量double length;  // 长double width;  // 宽// 成员函数// 设置长和宽public void Acceptdetails() {length = 4.5;  width = 3.5;}// 计算面积public double GetArea() {return length * width;}// 显示输出public void Display() {Console.WriteLine("Length: {0}", length);   // 4.5Console.WriteLine("Width: {0}", width);     // 3.5Console.WriteLine("Area: {0}", GetArea());  // 15.75}}class ExecuteRectangle {static void Main(string[] args) {Rectangle r = new Rectangle();r.Acceptdetails();r.Display();Console.ReadKey();}}
}

1. 数据类型

C#语言的类型主要分为:值类型和引用类型,值类型的变量直接包含数据,而引用类型的变量存储数据的引用地址。

对于引用类型,两个变量可以引用同一对象,因此,对一个变量执行的运算可能会影响另一个变量所引用的对象。引用类型在托管堆分配空间。

对于值类型,每个变量都有自己的数据副本,对一个变量执行的操作不可能影响另一个。值类型在线程栈分配空间。

// 引入命名空间
using System;namespace Application
{// 引用类型public class SomeRef{public int X { get; set; }}// 值类型public struct SomeVal{public int X { get; set; }}class ValueTypeTest{static void Main(string[] args){SomeRef r1 = new SomeRef();  // 在堆上分配SomeVal v1 = new SomeVal();  // 在栈上分配r1.X = 5;  // 提领指针v1.X = 5;  // 在栈上修改Console.WriteLine(r1.X);  // 5Console.WriteLine(v1.X);  // 5SomeRef r2 = r1;  // 只复制引用SomeVal v2 = v1;  // 在栈上分配并赋值成员r1.X = 8;  // r1.x和r2.x都会修改v1.X = 8;  // v1.x会修改,v2.x不会Console.WriteLine(r1.X);  // 8Console.WriteLine(r2.X);  // 8Console.WriteLine(v1.X);  // 8Console.WriteLine(v2.X);  // 5Console.ReadKey();}}
}

1.1 值类型

值类型包含简单类型结构类型枚举类型

  • 简单类型

    • 有符号整型:sbyte、short、int、long
    • 无符号整型:byte、ushort、unit、ulong
    • Unicode字符:char
    • IEEE浮点型:float、doule
    • 高精度小数:decimal
    • 布尔型:bool
  • 结构类型
    • 用户自定义类型struct
  • 枚举类型
    • 用户自定义类型enum

1.2 引用类型

引用类型包含类类型接口类型数组类型委托类型

  • 类类型

    • 所有其他类型的最终基类:object
    • Unicode字符串:string
    • 用户自定义类型:class
  • 接口类型
    • 用户自定义类型:interface
  • 数组类型
    • 单维与多维数组:如int[]、int[,]
  • 委托类型
    • 用户自定义类型:delegate

1.3 指针类型

仅在不安全代码中提供,使用unsafe关键词,开启不安全代码开发模式后,代码可以使用指针、分配和释放内存块,以及使用函数指针调用方法。unsafe代码的安全需要开发人员自行检测。

可以定义为指针的类型有

  • sbyte、byte、short、unshort、int、uint、long、ulong、char、float、double、decimal、bool
  • 任何枚举类型
  • 任何指针类型
  • 仅包含非托管类型的字段的任何用户定义的结构类型

装箱和拆箱操作不支持指针,但是可在不同的指针类型之间以及指针类型与整型之间进行转换。指针不能指向引用或包含引用的结构,因为无法对对象引用进行垃圾回收。

指针类型声明示例:

  • int* p: p是指向整数的指针
  • int** p: p是指向整数的指针的指针
  • int*[] p: p是指向整数的指针的一维数组
  • char* p: p是指向字符的指针
  • void* p: p是指向未知类型的指针

对指针执行的运算符和语句

运算符/语句 使用 运算符/语句 使用
* 执行指针间接寻址 -> 通过指针访问结构的成员
[] 为指针建立索引 & 获取变量的地址
++-- 递增和递减指针 +- 执行指针算法
==!=<><=>= 比较指针 stackalloc 在堆栈上分配内存
fixed语句 临时固定变量以便找到其地址
using System;namespace Application
{class UnsafeTest{static void Main(string[] args){int[] a = new int[5] { 10, 20, 30, 40, 50 };unsafe // 开启不安全代码模式{fixed (int* p = &a[0])  // 设置指向托管变量的指针,在执行该语句期间固定此变量{int* p2 = p;Console.WriteLine(*p2);  // 10p2 += 1;  // 指针向后移Console.WriteLine(*p2);  // 20p2 += 1;  // 指针向后移Console.WriteLine(*p2);  // 30Console.WriteLine("------");Console.WriteLine(*p);  // 10*p += 1;  // 指针所指向的值加1Console.WriteLine(*p);  // 11*p += 1;  // 指针所指向的值加1Console.WriteLine(*p);  // 12}}Console.WriteLine("------");Console.WriteLine(a[0]);  // 12}}
}

1.4 装箱与拆箱

值类型转换为引用类型称为装箱引用类型转为值类型称为拆箱。注:只有被装过箱的对象才能拆箱。

using System;namespace Application
{class BoxingAndUnBoxingTest{static void Main(string[] args){int val = 100;object obj = val;  // 装箱操作Console.WriteLine("obj = {0}", obj);  // obj = 100int num = (int) obj;  // 拆箱操作Console.WriteLine("num = {0}", num);  // num = 100Console.ReadKey();}}
}

1.5 类型转换

类型转换是把数据从一种类型转换为另一种类型,C#中类型转换有两种形式:

  • 隐式类型转换:是C#默认的以安全的方式进行的转换,不会导致数据丢失,例如从小的整数类型转换为大的整数类型,从派生类转换为基类。
  • 显式类型转换-:即强制类型转换,需要强制转换运算符,而且强制转换会造成数据丢失。
using System;namespace Application
{class ForceTypeConversionTest{static void Main(string[] args){double d = 5673.74;int i;i = (int) d;  // double强制转换为intConsole.WriteLine(i);  // 5673Console.ReadKey();}}
}

下表为C#内置的一些类型转换方法

方法 描述 方法 描述
ToBoolean 转换为布尔型 ToByte 转换为字节类型
ToChar 转换为单个Unicode字符类型 ToDateTime 把整数或字符串类型转换为日期-时间结构
ToDecimal 把浮点型或整数型转换为十进制类型 ToDouble 转换为双精度浮点型
ToInt16 转换为16位整数类型 ToInt32 转换为32位整数类型
ToInt64 转换为64位整数类型 ToSbyte 转换为有符号字节类型
ToString 转换为字符串类型 ToType 转换为指定类型
ToUInt16 转换为16位无符号整数类型 ToUInt32 转换为32位无符号整数类型
ToUInt64 转换为64位无符号整数类型 ToSingle 转换为小浮点数类型

如把不同值的类型转换为字符串类型

using System;namespace Application
{class StringConversionTest{static void Main(string[] args){int a = 2022;float b = 8.005f;double c = 2345.6789;bool d = false;// 转换为字符串类型Console.WriteLine(a.ToString());  // 2022Console.WriteLine(b.ToString());  // 8.005Console.WriteLine(c.ToString());  // 2345.6789Console.WriteLine(d.ToString());  // FalseConsole.ReadKey();}}
}

2. 变量

变量是一种通过变量名表示某个内存区域的方法,变量的值可以更改并重复使用。

C#中基础变量类型可以归纳为以下几种:

类型 示例
整型 sbyte、byte、short、ushort、int、unit、long、ulong、char
浮点型 float、doule
十进制类型 decimal
布尔型 true、false
空类型 可为空值的数据类型

2.1 声明

声明变量的语法格式为<data_type> variable_list;

如:

int i, j, k;
double a;
char b, c;
bool d;

变量名需要遵循的规则:

  • 变量名中只可以包含英文字母a-z,A-Z、数字0-9和下划线_
  • 不能以数字开头
  • 变量名不能是任何C#的保留字或关键字

2.2 初始化

可以在声明时直接初始化,也可以在声明后另起一行进行初始化,如:

using System;namespace Application
{class VariableTest{static void Main(string[] args){short a;float b;double c, d = 5.5;// 初始化变量a = 10;b = 20.1f;c = a + b;Console.WriteLine("a = {0}, b = {1}, c = {2}, d = {3}", a, b, c, d);  // a = 10, b = 20.1, c = 30.1000003814697, d = 5.5Console.WriteLine("请输入一个数字:");// 接受用户输入的值int f = Convert.ToInt32(Console.ReadLine());Console.WriteLine(f);  // 输出你所输入数字Console.ReadKey();}}
}

3. 常量

常量是固定值,使用关键字const定义,在程序执行期间不允许修改,常量可以是任何基本数据类型,比如整数常量、浮点常量、字符常量、字符串常量、枚举常量。

3.1 定义

声明常量的语法格式为const <data_type> constant_name = value;,常量在声明的同时需要赋值。

3.2 使用

using System;namespace Application
{class ConstantConversionTest{static void Main(string[] args){const double PI = 3.14;double r = 3;double area = PI * r * r;Console.WriteLine("半径为:{0},圆的面积为:{1}", r, area);  // 半径为:3,圆的面积为:28.26Console.ReadKey();}}
}

4. 运算符

运算符是一个用来告诉编辑器执行特定数字或逻辑运算的符号。

4.1 算术运算符

完成特定算术运算的符号:

算术运算符 描述 算术运算符 描述
+ 加法 % 取余
- 减法 ++ 自增
* 乘法 -- 自减
/ 除法
using System;namespace Application
{class ArithmeticOperatorsTest{static void Main(string[] args){int a = 10;int b = 20;int c;c = a + b;Console.WriteLine("a + b = {0}", c);  // a + b = 30c = a - b;Console.WriteLine("a - b = {0}", c);  // a - b = -10c = a * b;Console.WriteLine("a * b = {0}", c);  // a * b = 200c = a / b;Console.WriteLine("a / b = {0}", c);  // a / b = 0c = a % b;Console.WriteLine("a % b = {0}", c);  // a % b = 10c = ++a;Console.WriteLine("c = {0}, a = {1}", c, a);  // c = 11, a = 11c = --a;Console.WriteLine("c = {0}, a = {1}", c, a);  // c = 10, a = 10c = b++;Console.WriteLine("c = {0}, b = {1}", c, b);  // c = 20, b = 21c = b--;Console.WriteLine("c = {0}, b = {1}", c, b);  // c = 21, b = 20Console.ReadKey();}}
}

4.2 关系运算符

比较运算符左右两边操作数的符号,结果为true或false

关系运算符 描述 关系运算符 描述
== 检查是否相等 != 检查是否不等
> 检查左操作数是否大于右操作数 < 检查左操作数是否小于右操作数
>= 检查左操作数是否大于等于右操作数 <= 检查左操作数是否小于等于右操作数
using System;namespace Application
{class RelationalOperatorTest{static void Main(string[] args){int a = 10;int b = 20;if (a == b) {Console.WriteLine("a等于b");}else {Console.WriteLine("a不等于b");  // a不等于b}if (a < b) {Console.WriteLine("a小于b");  // a小于b}else{Console.WriteLine("a不小于b");}if (a > b) {Console.WriteLine("a大于b");}else{Console.WriteLine("a不大于b");  // a不大于b}a = 15;b = 33;if (a <= b) {Console.WriteLine("a小于或等于b");  // a小于或等于b}if (b >= a){Console.WriteLine("b大于或等于a");  // b大于或等于a}Console.ReadKey();}}
}

4.3 逻辑运算符

进行逻辑运算的符号,结果为truefalse

逻辑运算符 描述
&& 逻辑与,两个操作数都为true,结果才为true
`
! 逻辑非,操作数为true,结果为false,操作数为false,结果为true
using System;namespace Application
{class LogicalOperatorsTest{static void Main(string[] args){bool a = true;bool b = true;if (a && b) {Console.WriteLine("条件为真");  // 条件为真}if (a || b){Console.WriteLine("条件为真");  // 条件为真}a = false;b = true;if (a && b) {Console.WriteLine("条件为真");    }else{Console.Write("条件为假");  // 条件为假}if (!(a && b)){Console.WriteLine("条件为真");   // 条件为真  }Console.ReadKey();}}
}

4.4 位运算符

位运算符 描述 位运算符 描述
& 按位与,均为1时,结果才为1 ` `
^ 按位异或,两个位数值不同时,结果才为1 ~ 按位取反,9变为1,1变为0
<< 二进制左移指定位数 >> 二进制右移指定位数
using System;namespace Application
{class BitwiseOperatorsTest{static void Main(string[] args){int a = 60;int b = 13;int c = 0;c = a & b;Console.WriteLine("a & b = {0}", c);  // a & b = 12c = a | b;Console.WriteLine("a | b = {0}", c);  // a | b = 61c = a ^ b;Console.WriteLine("a ^ b = {0}", c);  // a ^ b = 49c = ~a;Console.WriteLine("~a = {0}", c);  // ~a = -61c = a << 2;Console.WriteLine("a << 2 = {0}", c);  // a << 2 = 240c = a >> 2;Console.WriteLine("a >> 2 = {0}", c);  // a >> 2 = 15Console.ReadKey();}}
}

4.5 赋值运算符

赋值运算符 描述 赋值运算符 描述
= 右边赋值给左边 += 左边+右边赋值给左边
-= 左边-右边赋值给左边 *= 左边*右边赋值给左边
/= 左边/右边赋值给左边 %= 左边%右边赋值给左边
<<= 左移且赋值给左边 >>= 右移且赋值给左边
&= 按位与且赋值给左边 ^= 按位异或且赋值给左边
` =` 按位或且赋值给左边
using System;namespace Application
{class AssignmentOperatorsTest{static void Main(string[] args){int a = 21;int c;c = a;Console.WriteLine("c = {0}", c);  // c = 21c += a;Console.WriteLine("c = {0}", c);  // c = 42c -= a;Console.WriteLine("c = {0}", c);  // c = 21c *= a;Console.WriteLine("c = {0}", c);  // c = 441c /= a;Console.WriteLine("c = {0}", c);  // c = 21c = 200;c %= a;Console.WriteLine("c = {0}", c);  // c = 11c <<= a;Console.WriteLine("c = {0}", c);  // c = 44c >>= a;Console.WriteLine("c = {0}", c);  // c = 11c &= 2;Console.WriteLine("c = {0}", c);  // c = 2c ^= 2;Console.WriteLine("c = {0}", c);  // c = 0c |= 2;Console.WriteLine("c = {0}", c);  // c = 2Console.ReadKey();}}
}

4.6 其他运算符

其他运算符 描述 其他运算符 描述
sizeof() 返回数据类型的大小 typeof() 返回class的类型
& 返回变量的地址 * 变量的指针
is 判断对象是否为某一类型 as 强制转换,即使失败也不抛异常
?: 三元运算符
using System;namespace Application
{class AssignmentOperatorsTest{static void Main(string[] args){Console.WriteLine("int的大小是{0}", sizeof(int));  // int的大小是4Console.WriteLine("short的大小是{0}", sizeof(short));  // short的大小是2Console.WriteLine("double的大小是{0}", sizeof(double));  // double的大小是8int a, b;a = 10;b = (a == 1) ? 20 : 30;Console.WriteLine("b的值是{0}", b);  // b的值是30Console.ReadKey();}}
}

5. 条件控制

5.1 if语句

由一个布尔表达式后跟一个或多个语句组成。

using System;namespace Application
{class IfStatementTest{static void Main(string[] args){int a = 20;// 使用if语句检查布尔条件,如果为真则进入if语句内if (a > 10) {Console.WriteLine("a大于10");  // a大于10}Console.WriteLine("a = {0}", a);  // a = 20Console.ReadKey();}}
}

5.2 if…else语句

一个if语句后跟一个可选的else语句,else语句在布尔表达式为false时执行。

using System;namespace Application
{class IfElseStatementTest{static void Main(string[] args){int a = 5;// 使用if语句检查布尔条件,如果为真则进入if语句内,否则进入else语句内if (a > 10) {Console.WriteLine("a大于10");}else {Console.WriteLine("a小于或等于10");  // a小于或等于10}Console.WriteLine("a = {0}", a);  // a = 5Console.ReadKey();}}
}

5.3 if…else if…else语句

一个if语句后可跟一个可选的else if ... else语句,用于测试多个条件:

  • 一个if后可跟零个或一个else,它必须在所有的else if之后
  • 一个if后可跟两个或多个else if,它们必须在else之前
  • 一旦某个else if匹配成功,其他else ifelse将不会被测试
using System;namespace Application
{class IfElseIFElseStatementTest{static void Main(string[] args){int a = 5;if (a == 10) {Console.WriteLine("a等于10");}else if (a > 10){Console.WriteLine("a大于10");}else{Console.WriteLine("a小于10");  // a小于10}Console.WriteLine("a = {0}", a);  // a = 5Console.ReadKey();}}
}

5.4 嵌套if语句

嵌套if语句意味着你可以在一个ifelse if语句内使用另一个ifelse if语句。

using System;namespace Application
{class NestedIfStatementTest{static void Main(string[] args){int a = 100;int b = 200;if (a == 10) {if (b == 200) {Console.WriteLine("a = 100, b = 200");  // a = 100, b = 200}}Console.ReadKey();}}
}

5.5 switch语句

一个switch语句允许测试一个变量等于多个值时的情况。

using System;namespace Application
{class SwitchStatementTest{static void Main(string[] args){int day = 5;switch (day){case 1:Console.WriteLine("Monday");break;case 2:Console.WriteLine("Tuesday");break;case 3:Console.WriteLine("Wednesday");break;case 4:Console.WriteLine("Thurday");break;case 5:Console.WriteLine("Friday");  // Fridaybreak;case 6:Console.WriteLine("Saturday");break;case 7:Console.WriteLine("Sunday");break;}Console.ReadKey();}}
}

5.6 嵌套switch语句

可以在一个switch语句内使用另一个switch语句。

using System;namespace Application
{class NestedSwitchStatementTest{static void Main(string[] args){int a = 100;int b = 200;switch (a){case 100:Console.WriteLine("a = 100");  // a = 100switch (b){case 200:Console.WriteLine("b = 200");  // b = 200break;}break;}Console.ReadKey();}}
}

5.7 ?条件运算符

条件运算符又称三元运算符,可以用来代替if...else语句,形式如Exp1 ? Exp2 : Exp3;

using System;namespace Application
{class TernaryOperatorTest{static void Main(string[] args){int a = 100;int b = a == 200 ? 100 : 300;/* 等价于int b;if (a == 200){b = 100;}else{b = 300;}*/Console.WriteLine("a = {0}, b = {1}", a, b);  // a = 100, b = 300Console.ReadKey();}}
}

6. 循环控制

循环语句允许多次执行一个语句或语句组。

6.1 while循环

只要给定条件为真,while循环语句会重复执行一个目标语句,while循环又称为当型循环,在执行循环主体之前测试条件。

using System;namespace Application
{class WhileStatementTest{static void Main(string[] args){int a = 3;while (a < 7){Console.WriteLine("a = {0}", a);a++;}// a = 3// a = 4// a = 5// a = 6Console.ReadKey();}}
}

6.2 for/foreach循环

for

for循环允许编写一个执行特定次数的循环的重复控制结构。

语法为:

for (init; condition; increment)
{statement(s);
}
using System;namespace Application
{class ForStatementTest{static void Main(string[] args){for (int a = 3; a < 7; a++){Console.WriteLine("a = {0}", a);}// a = 3// a = 4// a = 5// a = 6Console.ReadKey();}}
}

foreach

foreach可以迭代数组或者一个集合对象。

using System;namespace Application
{class ForeachStatementTest{static void Main(string[] args){int[] array = new int[]{3, 4, 5, 6};foreach (int num in array){Console.WriteLine("num = {0}", num);}// num = 3// num = 4// num = 5// num = 6Console.ReadKey();}}
}

6.3 do…while循环

do...while循环是在尾部检查循环条件,直到给定的条件变为假为止。do...while又称直到型循环,会保证循环体至少执行一次。

using System;namespace Application
{class DoWhileStatementTest{static void Main(string[] args){int a = 3;do{Console.WriteLine("a = {0}", a);a += 1;} while (a < 7);// a = 3// a = 4// a = 5// a = 6Console.ReadKey();}}
}

6.4 嵌套循环

C#允许在任何类型循环内嵌套任何类型的循环。

using System;namespace Application
{class NestedLoopStatementTest{static void Main(string[] args){for (int i = 1; i <= 9; i++){for (int j = 1; j <= i; j++){Console.Write("{0} * {1} = {2}\t", j, i, i * j);}Console.WriteLine();}Console.ReadKey();/*
1 * 1 = 1
1 * 2 = 2       2 * 2 = 4
1 * 3 = 3       2 * 3 = 6       3 * 3 = 9
1 * 4 = 4       2 * 4 = 8       3 * 4 = 12      4 * 4 = 16
1 * 5 = 5       2 * 5 = 10      3 * 5 = 15      4 * 5 = 20      5 * 5 = 25
1 * 6 = 6       2 * 6 = 12      3 * 6 = 18      4 * 6 = 24      5 * 6 = 30      6 * 6 = 36
1 * 7 = 7       2 * 7 = 14      3 * 7 = 21      4 * 7 = 28      5 * 7 = 35      6 * 7 = 42      7 * 7 = 49
1 * 8 = 8       2 * 8 = 16      3 * 8 = 24      4 * 8 = 32      5 * 8 = 40      6 * 8 = 48      7 * 8 = 56      8 * 8 = 64
1 * 9 = 9       2 * 9 = 18      3 * 9 = 27      4 * 9 = 36      5 * 9 = 45      6 * 9 = 54      7 * 9 = 63      8 * 9 = 72     9 * 9 = 81
*/}}
}

6.5 break语句

break语句有两种用法:

  • break语句出现在一个循环内时,循环会立即终止,跳出循环体,执行下一条语句
  • 可用于switch语句中终止一个case

注意,如果是嵌套循环,break循环只会跳出他所在的那一层循环。

using System;namespace Application
{class BreakStatementTest{static void Main(string[] args){int a = 3;while (a < 1000) {Console.WriteLine("a = {0}", a);a++;if (a > 6) {break;}}// a = 3// a = 4// a = 5// a = 6Console.ReadKey();}}
}

6.6 continue语句

continue语句会跳出当前循环中的代码,开始下一次循环。

using System;namespace Application
{class ContinueStatementTest{static void Main(string[] args){int a = 3;while (a < 7) {if (a == 4) {a++;continue;}Console.WriteLine("a = {0}", a);a++;}// a = 3// a = 5// a = 6Console.ReadKey();}}
}

7. 数组

数组是存储相同类型元素的固定大小的顺序集合,数组中某个指定元素是通过索引来访问的,所有的数组是有连续的内存位置组成,最低的地址对应第一个元素,最高的地址对应最后一个元素。

7.1 声明

声明语法为datatype[] arrayName;,其中datatype用于指定数组内元素的类型,[]指定数组的大小,arrayName指定数组的名字,如double[] balance;

7.2 初始化

声明一个数组不会在内存中初始化数组,当初始化数组遍历时,可以赋值给数组。

数组是一个引用类型,所有需要使用new关键字来创建数组的实例,如double[] balance = new double[10];

7.3 赋值

赋值有多种形式,可以通过索引号赋值给单个的数组元素,如balance[0] = 123.0;

也可以在声明的同时给数组赋值,如double[] balance = {123.0, 456.0, 789.0};

还可以创建并初始化一个数组,如int[] marks = new int[3]{1, 2, 3};,此处可以省略数组的大小,即int[] marks = new int[]{1, 2, 3};。

可以赋值给一个数组变量到另一个目标数组变量中,此时目标和源会指向相同的内存位置。

int[] marks = new int[]{1, 2, 3};
int[] numbers = marks;

7.4 访问数组元素

元素是通过带索引的数组名称来访问的,如int a = numbers[1];

using System;namespace Application
{class ArrayTest1{static void Main(string[] args){int[] num = new int[5];  // 声明并初始化// 循环赋值for (int i = 0; i < 5; i++){num[i] = i + 100;}// 循环访问、遍历for (int j = 0; j < 5; j++){Console.WriteLine("num[{0}] = {1}", j, num[j]);}// num[0] = 100// num[1] = 101// num[2] = 102// num[3] = 103// num[4] = 104Console.ReadKey();}}
}

除了使用for循环来访问数组中每个元素,还可以使用foreach语句

using System;namespace Application
{class ArrayTest2{static void Main(string[] args){int[] num = new int[5];  // 声明并初始化// 循环赋值for (int i = 0; i < 5; i++){num[i] = i + 100;}// 遍历foreach (int j in num){Console.WriteLine("num[{0}] = {1}", j - 100, j);}// num[0] = 100// num[1] = 101// num[2] = 102// num[3] = 103// num[4] = 104Console.ReadKey();}}
}

7.5 多维数组

C#支持多维数组,多维数组又称为矩形数组,二维数组是最简单最常用的多维数组,可以被认为是一个带有x行和y列的表格。

using System;namespace Application
{class toDArrayTest{static void Main(string[] args){// 声明初始化并赋值int[,] num = new int[3, 2] {{1, 2}, {3, 4}, {5, 6}};// 遍历for (int i = 0; i < 3; i++){for (int j = 0; j < 2; j++){Console.WriteLine("num[{0},{1}] = {2}", i, j, num[i, j]);}}// num[0,0] = 1// num[0,1] = 2// num[1,0] = 3// num[1,1] = 4// num[2,0] = 5// num[2,1] = 6Console.ReadKey();}}
}

7.6 交替数组

交替数组是数组的数组,是一维数组。

using System;namespace Application
{class AlternatingArrayTest{static void Main(string[] args){// 声明初始化并赋值int[][] num = new int[][] {new int[]{1, 2}, new int[]{3, 4}, new int[]{5, 6}};// 遍历for (int i = 0; i < 3; i++){for (int j = 0; j < 2; j++){Console.WriteLine("num[{0}][{1}] = {2}", i, j, num[i][j]);}}// num[0][0] = 1// num[0][1] = 2// num[1][0] = 3// num[1][1] = 4// num[2][0] = 5// num[2][1] = 6Console.ReadKey();}}
}

7.7 传递数组给函数

可以传递数组作为函数的参数,可以通过指定不带索引的数组名称来给函数传递一个指向数组的指针。

using System;namespace Application
{class ArrayAndFunctionTest{double getAverage(int[] arr, int size){int sum = 0;for (int i = 0; i < size; i++) {sum += arr[i];}return (double) sum / size;}static void Main(string[] args){int[] num = new int[]{1, 2, 3, 4};ArrayAndFunctionTest app = new ArrayAndFunctionTest();double avg = app.getAverage(num, 4);Console.WriteLine("avg = {0}", avg);  // avg = 2.5Console.ReadKey();}}
}

7.8 参数数组

当声明一个方法时,可能无法确定传递给函数的参数数目,此时,可以使用参数数组解决该问题,参数数组通常用于传递未知数量的参数给函数。C#提供了params关键字,是调用数组为形参的方法时,既可以传递数组实参,又可以传递一组数组元素。其格式为public 返回类型 方法名称(params 类型名称[] 数组名称)

注意:

  • params关键字的参数类型必须是一维数组,不能使用多维数组
  • 不能和refout同时使用
  • params关键字的参数必须是最后一个参数,并且只能运行一个params关键字
  • 不能使用params来使用重载方法
  • 没有params关键字的方法优先级高于到有params关键字的方法
using System;namespace Application
{class ArrayAndParamsTest{static void Test(int a, int b){Console.WriteLine("a + b = {0}", a + b);}static void Test(params int[] arr){foreach (int i in arr){Console.Write("{0} ", i);}Console.WriteLine();}static void Main(string[] args){Test(1, 2);  // a + b = 3Test(1, 2, 3, 4, 5);  // 1 2 3 4 5int[] arr = {1, 1, 2, 2};Test(arr);  // 1 1 2 2Console.ReadKey();}}
}

7.9 Array类

Array类是C#中所有数组的基类,他是在System命名空间定义的,Array类提供了各种用于数组的属性和方法。

属性

属性 描述
IsFixedSize 获取一个值,该值指示数组是否带有固定大小
IsReadOnly 获取一个值,该值指示数组是否只读
Length 获取一个32位整数,该值表示所有维度的数组的元素总数
LongLength 获取一个64位整数,该值表示所有维度的数组的元素总数
Rank 获取数组的维度

方法

方法 描述 方法 描述
Clear 根据元素类型,设置数组中某个范围的元素为零、false或null Copy(Array, Array, Int32) 从数组的第一个元素开始复制某个范围的元素到另一个数组的第一个元素位置
CopyTo(Array, Int32) 从当前的一维数组中复制所有元素到一个指定的一维数组的指定索引位置 GetLength 获取一个32位整数,表示指定维度的数组中的元素总和
GetLongLength 获取一个64位整数,表示指定维度的数组中的元素总和 GetLowerBound 获取数组中指定维度的下界
GetType 获取当前实例的类型 GetUpperBound 获取数组中指定维度的上界
GetValue(Int32) 获取一维数组中指定位置的值 IndexOf(Array, Object) 搜索指定的对象,返回整个一维数组中第一次出现的索引
Reverse(Array) 逆转整个一维数组中元素的顺序 SetValue(Object, Int32) 给一维数组中指定位置的元素设置值
Sort(Array) 使用数组的每个元素的IComparable接口实现排序 ToString 返回当前对象的字符串
using System;namespace Application
{class ArrayClassTest{static void Main(string[] args){int[] list = {3, 2, 5, 8, 7, 6};Console.Write("原始数组:");foreach (int i in list){Console.Write(i + " ");}Console.WriteLine();Array.Reverse(list);Console.Write("逆转数组:");foreach (int i in list){Console.Write(i + " ");}Console.WriteLine();Array.Sort(list);Console.Write("排序数组:");foreach (int i in list){Console.Write(i + " ");}Console.WriteLine();Console.ReadKey();// 原始数组:3 2 5 8 7 6 // 逆转数组:6 7 8 5 2 3 // 排序数组:2 3 5 6 7 8}}
}

7.10 Array、ArrayList与泛型List<T>的区别

Array

数组Array是一个存储相同类型元素的固定大小的顺序集合,数组是用来存储数据的集合,通常认为数组是一个同一类型变量的集合。

Array类是C#中所有数组的基类,在System空间定义的。

数组在内存中是连续存储的,所以优点是索引速度很快,赋值和修改元素也非常简单。

缺点是声明数组的时候必须指定数组的长度;如果数组太长会造成内存浪费,太短会造成数据溢出的错误;在数组两个数据间插入数据很麻烦。

ArrayList

为了解决数组的优点,C#提供了ArrayList对象,ArrayList是在命名空间System.Collections下,使用该类时必须进行引用,同时继承了IList接口,提供了数据存储和检索。

ArrayList对象的大小是按照其中存储的数据来动态扩充与收缩的,不需要再声明时指定长度。

ArrayList的默认初始容量是0,随着元素添加到ArrayList中,容量会根据需要重新分配自动增加。

ArrayList解决了前面提到的Array数组的所有缺点,但是在存储或检索值类型时通常会发生装箱和拆箱操作,带来很大性能损耗,不是类型安全的。

using System;
using System.Collections;
namespace Application
{class ArrayListTest{static void Main(string[] args){ArrayList list = new ArrayList();list.Add("Hello");list.Add("World");list.Add("!");Console.WriteLine("Count: {0}, Capacity: {1}", list.Count, list.Capacity);PrintValues(list);Console.ReadKey();// Count: 3, Capacity: 4// Hello World !}public static void PrintValues(IEnumerable list){foreach (object obj in list){Console.Write("{0} ", obj);}Console.WriteLine();}}
}

泛型List<T>

由于ArrayList存在不安全类型与装箱拆箱的确定,所有出现了List类,该类使用大小可按需动态增加的数组实现IList泛型接口,是类型安全的,在声明List集合时,必须为其List集合内数据声明数据类型。

using System;
using System.Collections.Generic;
namespace Application
{class ListTest{static void Main(string[] args){List<string> list = new List<string>();list.Add("Hello");list.Add("World");list.Add("!");Console.WriteLine("Count: {0}, Capacity: {1}", list.Count, list.Capacity);foreach (string e in list){Console.Write("{0} ", e);}Console.WriteLine();Console.ReadKey();// Count: 3, Capacity: 4// Hello World !}}
}

总结

  • 数组Array的容量固定,而ArrayList和泛型List<T>的容量可自动扩充

  • 数组Array可有多个维度,而ArrayList和泛型List<T>始终只有一个维度

  • 特定类型的数组Array性能由于ArrayList的性能

  • ArrayList和泛型List<T>基本等同,如果List<T> 类的类型T是引用类型,则两个类的行为是完全相同的。如果T是值类型,需要考虑装箱和拆箱造成的性能损耗,List<T> 是类型安全

8. 枚举

枚举是一种类型,适用于某些取值范围有限的数据,枚举类型是使用enum关键字声明的,默认访问权限和类一样,都是internal,枚举的每一个值都是一个整型,默认是从0开始。

8.1 声明

声明的一般语法为 [访问权限修饰符] enum <enum_name> { enumeration list }

其中enum_name为枚举的类型名称,遵循大驼峰命名法,enumeration list是一个用逗号分割的标识符列表,如

public enum Days
{Sun, Mon, Tue, Wed, Thu, Fri, Sat
}

8.2 访问

using System;namespace Application
{class EnumTest{enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat}static void Main(string[] args){Console.WriteLine(Days.Sun);  // SunConsole.WriteLine((int)Days.Sun);  // 5Console.WriteLine((Days)4);  // ThuDays a = (Days)4;Console.WriteLine(a.ToString());  // ThuConsole.ReadKey();}}
}

9. 字符串

9.1 创建String对象

使用以下方法来创建String对象:

  • 通过给String变量指定一个字符串
  • 通过使用String类构造函数
  • 通过使用字符串串联符号+
  • 通过检索属性或调用一个返回字符串的方法
  • 通过格式化方法开转换一个值或对象为它的字符串表示形式
using System;namespace Application
{class StringCreateTest{static void Main(string[] args){// 指定一个字符串string fname, lname;fname = "Harry";lname = "Peter";// 通过+符号连接string fullname = fname + lname;Console.WriteLine("Full Name: {0}", fullname);  // Full Name: HarryPeter// 使用string构造函数char[] letters = {'H', 'e', 'l', 'l', 'o'};string greetings = new string(letters);Console.WriteLine("Greetings: {0}", greetings);  // Greetings: Hello// 方法返回字符串string[] sarray = {"Hello", "From", "C#"};string message = String.Join(" ", sarray);Console.WriteLine("Message: {0}", message);  // Message: Hello from C#// 用于转化值的格式化方法DateTime waiting = new DateTime(2022, 10, 10, 21, 1, 1);string chat = String.Format("Message sent at {0:t} on {0:D}", waiting);Console.WriteLine("Message: {0}", chat);  // Message: Message sent at 9:01 on Monday, October 10, 2022Console.ReadKey();}}
}

9.2 属性和方法

String类有两个属性:

  • Chars:在当前String对象中获取Char对象的指定位置
  • Length:在当前String对象中获取字符数

String类的方法:

方法名称 描述 方法名称 描述
public static int Compare(string strA, string strB) 比较两个指定的string对象,并返回一个表示他们在排列顺序中相对位置的整数。该方法区分大小写 public static int Compare(string strA, string strB, bool ignoreCase) 比较两个指定的string对象,并返回一个表示他们在排列顺序中相对位置的整数。该方法不区分大小写
public static string Concat(string str0, string str1) 连接两个string对象 public static string Concat(string str0, string str1, string str2) 连接三个string对象
public static string Concat(string str0, string str1, string str2, string str3) 连接四个string对象 public bool Contains(string value) 返回一个表示指定string对象是否出现在字符串中的值
public static string Copy(string str) 创建一个与指定字符串具有相同值的新string对象 public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) 从string对象的指定位置开始复制指定数量的字符到Unicode字符数组中的指定位置
public bool EndsWith(string value) 判断string对象的结尾是否匹配指定的字符串 public bool Equals(string value) 判断当前的string对象是否与指定的string对象具有相同的值
public static bool Equals(string value) 判断两个指定的string对象是否具有相同的值 public static string Format(string format, Object arg0) 把指定字符串中一个或多个格式项替换为指定对象的字符串表示形式
public int IndexOf(char value) 返回指定Unicode字符在当前字符串中第一次出现的索引,索引从0开始 public int IndexOf(string value) 返回指定字符串在该实例中第一次出现的索引,索引从0开始
public int IndexOf(char value, int startIndex) 返回指定Unicode字符在当前字符串中指定字符位置开始搜索第一次出现的索引,索引从0开始 public int IndexOf(string value, int startIndex) 返回指定字符串在当前字符串中指定字符位置开始搜索第一次出现的索引,索引从0开始
public int IndexOfAny(char[] anyOf) 返回某一个指定的Unicode字符数组中任意字符在该实例中第一次出现的索引,索引从0开始 public int IndexOfAny(char[] anyOf, int startIndex) 返回某一个指定的Unicode字符数组从任意字符指定字符位置开始搜索第一次出现的索引,索引从0开始
public string Insert(int startIndex, string value) 返回一个新的字符串,其中,指定的字符串被插入在当前string对象的指定索引位置 public static bool IsNullOrEmpty(string value) 指示指定的字符串是否为null或者是否为一个空的字符串
public static string Join(string separator, string[] value) 连接一个字符串数组中的所有元素,使用指定的分隔符分割每个元素 public static string Join(string separator, string[] value, int startIndex, int count) 连接一个字符串数组中从指定位置开始的元素,使用指定的分隔符分割每个元素
public int LastIndexOf(char value) 返回指定Unicode字符在当前string对象中最后一次出现的索引位置,索引从0开始 public int LastIndexOf(string value) 返回指定字符串在当前string对象中最后一次出现的索引位置,索引从0开始
public string Remove(int startIndex) 移除当前实例中的所有字符,从指定位置开始,一直到最后一个位置为止,并返回字符串 public string Remove(int startIndex, int count) 从当前字符串的指定位置开始移除指定数量的字符,并返回字符串
public string Replace(char oldChar, char newChar) 把当前string对象中,所有指定的Unicode字符替换为另一个Unicode字符,并返回新的字符串 public string Replace(string oldValue, string newValue) 把当前string对象中,所有指定的字符串替换为另一个指定的字符串,并返回新的字符串
public string[] Split(params char[] separator) 返回一个字符串数组,包含当前的string对象中的子字符串,子字符串是按照指定的Unicode字符数组中的元素进行分隔的 public string[] Split(char[] separator, int count) 返回一个字符串数组,包含当前的string对象中的子字符串,子字符串是按照指定的Unicode字符数组中的元素进行分隔的,count指定要返回的子字符串最大数目
public bool StartsWith(string value) 判断字符串实例的开头是否匹配指定的字符串 public char[] ToCharArray() 返回一个带有当前string对象中所有字符的Unicode字符数组
public char[] ToCharArray() 返回一个带有当前string对象中所有字符的Unicode字符数组,从指定索引开始,知道到达指定的长度为止 public string ToLower() 把字符串转换为小写并返回
public string ToUpper() 把字符串转换为大写并返回 public string Trim() 移除当前string对象中前后所有的空白字符
using System;namespace Application
{class StringMethodsTest{static void Main(string[] args){string str1 = "This is text";string str2 = "This is test";if (String.Compare(str1, str2) == 0) {Console.WriteLine(str1 + " and " + str2 + " are equal.");}else{Console.WriteLine(str1 + " and " + str2 + " are not equal.");// This is text and This is test are not equal.}if (str1.Contains("text")){Console.WriteLine("The sequence 'text' was found.");// The sequence 'text' was found.}string substr = str1.Substring(8);Console.WriteLine(substr); // textstring[] strarray = new string[]{"Hello world", "Hello Csharp", "Hello Sunday"};string str = String.Join("\n", strarray);Console.WriteLine(str);// Hello world// Hello Csharp// Hello SundayConsole.ReadKey();}}
}

10. 命名空间

命名空间的设计目的是提供一种让一组名称与其他名称分隔的方式,可以使一个命名空间中声明的类名与另一个命名空间中声明的同类名不冲突。举个例子,一个文件夹中可以包含多个文件夹,每一个文件夹中不能有相同的文件名,但是不同文件夹中的文件可以重名。

10.1 定义

以关键字namespace开始,后跟命名空间的名字

using System;namespace FirstSpace
{class ClassA{public void MyFunc(){Console.WriteLine("Inside FirstSpace");}}
}namespace SecondSpace
{class ClassA{public void MyFunc(){Console.WriteLine("Inside SecondSpace");}}
}
class TestClass
{static void Main(string[] args){FirstSpace.ClassA fc = new FirstSpace.ClassA();SecondSpace.ClassA sc = new SecondSpace.ClassA();fc.MyFunc();  // Inside FirstSpacesc.MyFunc();  // Inside SecondSpaceConsole.ReadKey();}
}

10.2 using的用法

  • using指令:可以引入命名空间,如:
using System;
using System.Collections.Generic;
  • using static指令:无需指定类型名称即可访问其静态成员的类型。
using static System.Math;
var = PI;
  • 起别名

using Project = PC.MyCompany.Project;

  • using语句:将实例与代码绑定。
using (Font font3 = new Font("Arial", 10.0f),font4 = new Font("Arial", 10.0f))
{// Use font3 and font4.
}
// 代码段结束时,自动调用font3和font4的Dispose方法,释放实例

10.3 嵌套命名空间

命名空间可以被嵌套,即可以在一个命名空间内部定义另一个命名空间,使用点运算符访问嵌套命名空间的成员。

using System;
using SomeNameSpace;
using SomeNameSpace.Nested;namespace SomeNameSpace
{public class ClassA{static void Main(string[] args){Console.WriteLine("Inside SomeNameSpace");  // Inside SomeNameSpaceNested.ClassB.MyFunc();  // Inside NestedConsole.ReadKey();}}// 内嵌命名空间namespace Nested{public class ClassB{public static void MyFunc(){Console.WriteLine("Inside Nested");}} }
}

11. 类

类是对象的蓝图,对象是类的实例,构成类的方法和变量称为类的成员。

11.1 定义

类的定义是以关键字class开始的,后跟类的名称,类的主体包含在一对花括号里面,主体的组成可能包括构造函数、析构函数、成员变量、成员函数,如:

<access specifier> class class_name
{// member variables<access specifier> <data type> variable1;<access specifier> <data type> variable2;// member methods<access specifier> <return type> method1(parameter_list){// method body}<access specifier> <return type> method2(parameter_list){// method body}
}
  • 访问标识符<access specifier>指定了对类及其成员的访问权限,如果没有指定,则使用默认的访问标志符,类的默认访问标识符是internal,成员的默认访问标识符是private

    • 数据类型<data type>指定了变量的类型,返回类型<return type>指定了方法返回的数据类型
  • 如果要访问类的成员,使用.运算符,其链接了对象的名称和成员的名称。
using System;
namespace BoxApplication
{class Box{public double length;public double width;public double height;public double GetVolume(){return length * width * height;}}class Boxtest{static void Main(string[] args){Box box1 = new Box();Box box2 = new Box();double volume = 0.0;box1.length = 5.0;box1.width = 6.0;box1.height = 7.0;box2.length = 10.0;box2.width = 11.0;box2.height = 12.0;volume = box1.GetVolume();Console.WriteLine("Box1的体积:{0}", volume);  // Box1的体积:210volume = box2.GetVolume();Console.WriteLine("Box2的体积:{0}", volume);  // Box2的体积:1320Console.ReadKey();}}
}

11.2 成员函数和封装

类的成员函数是在类内部定义的函数,能在类的任何对象上操作,其能访问该对象的所有成员。

类的成员变量是对象的属性,且保持私有来实现封装,这些变量只能使用公共成员函数来访问。

using System;
namespace BoxApplication
{class Box{private double length;private double width;private double height;public void SetLength(double value){length = value;}public void SetWidth(double value){width = value;}public void SetHeight(double value){height = value;}public double GetVolume(){return length * width * height;}}class Boxtest{static void Main(string[] args){Box box1 = new Box();Box box2 = new Box();double volume = 0.0;box1.SetLength(5.0);box1.SetWidth(6.0);box1.SetHeight(7.0);box2.SetLength(10.0);box2.SetWidth(11.0);box2.SetHeight(12.0);volume = box1.GetVolume();Console.WriteLine("Box1的体积:{0}", volume);  // Box1的体积:210volume = box2.GetVolume();Console.WriteLine("Box2的体积:{0}", volume);  // Box2的体积:1320Console.ReadKey();}}
}

11.3 构造函数

类的构造函数是类的一个特殊的成员函数,当创建类的新对象时执行。

构造函数的名称与类的名称完全相同,他没有任何返回类型。

默认的构造函数没有任何参数,但是可以定义一个带参数的构造函数来给对象赋初值。

using System;
namespace LineApplication
{class Line{private double length;// 构造函数public Line(double value){length = value;Console.WriteLine("对象已经创建,length = {0}", value);}public void SetLength(double value){length = value;}public double GetLength(){return length;}static void Main(string[] args){Line line = new Line(10.0);  // 对象已经创建,length = 10Console.WriteLine("线条的长度:{0}", line.GetLength());  // 线条的长度:10line.SetLength(6.0);Console.WriteLine("线条的长度:{0}", line.GetLength());  // 线条的长度:6Console.ReadKey();}}
}

11.4 析构函数

类的析构函数是类的一个特殊的成员函数,当类的对象超出范围时执行。

析构函数的名称是在类的名称前加一个~符号,没有返回值也不接收任何参数。

析构函数用于在结束程序(如关闭文件、释放内存等)之前释放资源。

析构函数不能继承或重载。

using System;
namespace LineApplication
{class Line{private double length;// 构造函数public Line(){Console.WriteLine("对象已创建");}~Line(){Console.WriteLine("对象已删除");}public void SetLength(double value){length = value;}public double GetLength(){return length;}static void Main(string[] args){Line line = new Line();  // 对象已创建line.SetLength(6.0);Console.WriteLine("线条的长度:{0}", line.GetLength());  // 线条的长度:6Console.ReadKey();}// 对象已删除}
}

11.5 静态成员

使用关键字static可以把类成员定义为静态的,当声明一个类成员为静态时,意味着只有一个该静态成员的副本。

关键字static意味着类中只有一个该成员的实例,静态变量用于定义常量,因为他们的值可以通过直接调用类而不需要创建类的实例来获取。

静态变量可在成员函数或类定义外部进行初始化,也可以在内部初始化。

将类成员函数声明为public static无需实例化类即可调用类成员函数。

using System;
namespace StaticApplication
{class StaticVar{public static int num;public void Count(){num++;}public static int GetNum(){return num;}}class StaticTest{static void Main(string[] args){StaticVar s1 = new StaticVar();StaticVar s2 = new StaticVar();s1.Count();s2.Count();s1.Count();Console.WriteLine("变量num: {0}", StaticVar.GetNum());  // 变量num: 3Console.ReadKey();}}
}

12. 结构体

结构体是值类型的数据结果,用来代表一个记录,可是单一变量存储各种数据类型的相关数据。

12.1 定义

使用struct关键字来创建结构体,struct语句为程序定义了一个带有多个成员的结构体。

using System;
using System.Text;
// 定义结构体
struct Books
{public string title;public string author;public string subject;public double price;
}public class StructureTest
{public static void Main(string[] args){Books book1;Books book2;book1.title = "C#";book1.author = "Microsoft";book1.subject = "C# Programing Guider";book1.price = 49.5;book2.title = "Harry Peter";book2.author = "Lolly";book2.subject = "Death Cup";book2.price = 88.9;Console.WriteLine("book1 title: {0}", book1.title);  // book1 title: C#Console.WriteLine("book1 price: {0}", book1.price);  // book1 price: 49.5Console.WriteLine("book2 author: {0}", book2.author);  // book2 author: LollyConsole.WriteLine("book2 subject: {0}", book2.subject);  // book2 subject: Death CupConsole.ReadKey();}
}

12.2 特点

  • 结构体可带方法、字段、索引、属性、运算符方法和事件
  • 结构体可定义带参数的构造函数,不能定义析构函数
  • 结构体不能继承其他的结构或类
  • 结构体不能作为其他结构或类的基础
  • 结构可实现一个或多个接口
  • 结构体成员不能指定为abstractvirtualprotected
  • 使用New操作符创建一个结构对象时,会调用适当的构造函数来创建结构体,结构体可以不使用New操作符即可被实例化
  • 不使用New操作符,只有在所有的字段都被初始化之后,字段才能被赋值,对象才可用

12.3 类与结构体的区别

  • 类是引用类型,在堆中分配空间,栈中保存的只是引用,结构体是值类型,在栈中分配空间
  • 结构体不支持继承
  • 结构体不能声明默认的构造函数
using System;
using System.Text;
// 定义结构体
struct Books
{public string title;public string author;public string subject;public double price;public void SetValues (string t, string a, string s, double p){title = t;author = a;subject = s;price = p;}public void Display(){Console.WriteLine("title: {0}", title);Console.WriteLine("author: {0}", author);Console.WriteLine("subject: {0}", subject);Console.WriteLine("price: {0}", price);}
}public class StructureTest
{public static void Main(string[] args){Books book1 = new Books();Books book2 = new Books();book1.SetValues("C#", "Microsoft", "C# Programing Guider", 49.5);book2.SetValues("Harry Peter", "Lolly", "Death Cup", 88.9);book1.Display();// title: C#// author: Microsoft// subject: C# Programing Guider// price: 49.5book2.Display();// title: Harry Peter// author: Lolly// subject: Death Cup// price: 88.9Console.ReadKey();}
}

13. 接口

接口定义了所有类继承接口时应该遵循的语法合同,接口定义了语法合同“是什么”部分,派生类定义了语法合同”怎么做“部分。

接口定义了属性、方法和事件成员,接口只包含成员的声明,成员的定义是派生类的责任。

接口使得实现接口的类或结构在形式上保持一致。

抽象类在某种程度上与接口类似,但是他们只是用在当只有少数方法由基类声明有派生类实现时。

抽象类不能直接实例化,但允许派生出具体的,具有实际功能的类。接口本身是不实现任何功能。

注意:

  • 接口方法不能用publicprivateabstarct等访问修饰符修饰,接口内不能有字段变量、构造函数
  • 接口内可以定义属性
  • 实现接口时,必须和接口的格式一致
  • 必须实现接口的所有方法
  • 接口是解决C#的单继承问题,使类可以同时继承多个基类

13.1 定义

使用关键字interface声明,默认是public的,接口名通常以I字母开头,如:

using System;
// 定义接口
interface IMyInterface
{// 接口成员,只有声明,无具体实现void MethodImplement();
}class InterfaceImplementer : IMyIterface
{static void Main(){InterfaceImplementer iImp = new InterfaceImplementer();iImp.MethodImplement();  // Hello My IterfaceConsole.ReadKey();}public void MethodImplement(){Console.WriteLine("Hello My Iterface");}
}

13.2 继承

如果一个接口继承其他接口,那么实现类或结构需要实现所有接口的成员。

using System;
interface IParentInterface
{void ParentInterfaceMethod();
}interface IMyInterface : IParentInterface
{void MethodToImplement();
}class InterfaceImplementer : IMyInterface
{static void Main(){InterfaceImplementer iImp = new InterfaceImplementer();iImp.MethodToImplement();  // MethodToImplement() callediImp.ParentInterfaceMethod();  // ParentInterfaceMethod() calledConsole.ReadKey();}public void MethodToImplement(){Console.WriteLine("MethodToImplement() called");}public void ParentInterfaceMethod(){Console.WriteLine("ParentInterfaceMethod() called");}
}

14. 方法

C#中的方法(也称为函数)是一段具有签名(由函数名、参数类型和参数修饰符组成的函数信息)的代码块,用来实现特定的功能,一般组成:

  • 访问权限修饰符:用于指定函数对一个类的可见性
  • 返回值类型:用于指定函数返回值的数据类型
  • 函数名称:用于进行函数调用的唯一名称
  • 参数列表:在调用函数时需要传递给函数的参数,参数列表是可选的,可为空
  • 函数主题:其中包含了实现函数功能的若干代码

14.1 方法声明

方法声明语法如下:

<access_specifier> <return_type> <method_name>(parameter_list)
{method_body;return statement;
}

其中,<access_specifier>为访问修饰符,<return_type>为返回值类型,<method_name>为方法名称,paramater_list为参数列表,method_body为方法主体,return statement为返回语句。访问权限修饰符、参数列表和返回语句是可选的。

class MethodTest
{public int GetMax(int num1, int num2){int result;result = num1 > num2 ? num1 : num2;return result;}
}

14.2 方法调用

using System;
namespace Application
{class MethodTest{public int GetMax(int num1, int num2){int result;result = num1 > num2 ? num1 : num2;return result;}static void Main(string[] args){int a = 100;int b = 200;int res;MethodTest m = new MethodTest();res = m.GetMax(a, b);Console.WriteLine("最大值是: {0}", res);  // 最大值是: 200Console.ReadKey();}}
}

14.3 递归调用

递归调用是方法自己调用自己。

using System;
namespace Application
{class NumberManipulator{public int factorial(int num){int res;res = num == 1 ? 1 : factorial(num - 1) * num;return res;}static void Main(string[] args){var m = new NumberManipulator();Console.WriteLine("5! = {0}", m.factorial(5));  // 5! = 120Console.WriteLine("7! = {0}", m.factorial(7));  // 7! = 5040Console.ReadKey();}}
}

14.4 参数传递

不带参数的方法声明时return_typevoid,当调用带有参数的方法时,需要向方法传递参数。C#有三种参数传递方式:

方式 描述
值参数 这种方式复制参数的实际值给函数的形式参数,实参和形参使用的是两个不同内存中的值。形参的值改变不会影响到实参的值,可以保证实参数据安全
引用参数 这种方法复制参数的内存位置引用给形式参数,意味着形参值的改变也会改变实参的值,使用ref关键字声明引用参数
输出参数 这个方式可以返回多个值,使用out关键字声明输出参数,可以不用return语句从函数中返回多个值

14.5 按值传递参数

参数默认的传递参数,这种方式实参和形参使用的是两个不同内存中的值。形参的值改变不会影响到实参的值,可以保证实参数据安全。

using System;
namespace Application
{class NumberManipulator{// 交换两个参数的值public void Swap(int num1, int num2){num1 = num1 ^ num2;num2 = num1 ^ num2;num1 = num1 ^ num2;}static void Main(string[] args){int a = 100;int b = 200;var m = new NumberManipulator();Console.WriteLine("调用方法之前,a = {0}, b = {1}", a, b);m.Swap(a, b);Console.WriteLine("调用方法之后,a = {0}, b = {1}", a, b);Console.ReadKey();// 调用方法之前,a = 100, b = 200// 调用方法之后,a = 100, b = 200}}
}

14.6 按引用传递参数

引用参数是一个对变量的内存位置的引用,引用参数表示与提供给方法的实际参数具有相同的内存位置,因此,引用参数值的改变也会改变实际参数的值,需要使用ref关键字声明引用参数。

using System;
namespace Application
{class NumberManipulator{// 交换两个参数的值public void Swap(ref int num1, ref int num2){num1 = num1 ^ num2;num2 = num1 ^ num2;num1 = num1 ^ num2;}static void Main(string[] args){int a = 100;int b = 200;var m = new NumberManipulator();Console.WriteLine("调用方法之前,a = {0}, b = {1}", a, b);m.Swap(ref a, ref b);Console.WriteLine("调用方法之后,a = {0}, b = {1}", a, b);Console.ReadKey();// 调用方法之前,a = 100, b = 200// 调用方法之后,a = 200, b = 100}}
}

14.7 按输出传递参数

return语句可用于只从函数中返回一个值,但是,可以使用输出参数来从函数中返回多个值,输出参数会把方法输出的数据赋给自己,提供给输出参数的变量不需要赋值。当需要从一个参数没有指定初始值的方法中返回值时,输出参数特别有用,需要使用out关键字声明引用参数。

using System;
namespace Application
{class NumberManipulator{// 交换两个参数的值public void GetValues(out int x, out int y){x = 13;y = 14;}static void Main(string[] args){int a, b;var m = new NumberManipulator();m.GetValues(out a, out b);Console.WriteLine("a = {0}, b = {1}", a, b);Console.ReadKey();// a = 13, b = 14}}
}

15. 封装

封装指把一个或多个项目封装在一个物理或逻辑的包中,封装是为了防止对实现细节的访问。

根据封装的具体需要,可以通过访问修饰符来设置使用者的访问权限。

  • public : 任何公有成员都可以被外部的类访问
  • private : 只有同一个类中的函数可以访问他的私有成员
  • protected:该类内部和继承类中可以访问
  • internal : 同一个程序集的对象可以访问
  • protected internal : protectedinternal的并集,符合任意一条都可以访问
using System;
namespace RectangelApplication
{class Rectangle{// 成员变量internal double length;internal double width;private double GetArea(){return length * width;}public void Display(){Console.WriteLine("面积:{0}", GetArea());}}class ExecuteRectangle{static void Main(string[] args){Rectangle r = new Rectangle();r.length = 4.0;r.width = 5.0;r.Display();  // 面积:20Console.ReadKey();}}
}

16. 继承

继承是面向对象程序的特征之一,继承允许根据一个类来定义另一个类,有助于维护和创建程序,从而利于重用代码节省开发时间。

创建一个新类时,新类允许继承一个已有的类,通过继。承可以创建新类时重用、扩展和修改被继承类中定义的成员,被继承的类称为“基类”,继承基类的类称为“派生类”。

注意,C#只支持单继承,派生类只能继承一个基类,但是继承是可以传递的。

16.1 基类与派生类

派生类继承了基类的成员变量和成员方法。因此父类对象应在子类对象创建之前被创建。您可以在成员初始化列表中进行父类的初始化。

创建派生类的语法:

<访问修饰符> class <基类>
{...
}
class <派生类> : <基类>
{...
}

举个例子

using System;namespace Application
{class Rectangle{protected double length;protected double width;public Rectangle(double l, double w){length = l;width = w;}public double GetArea(){return length * width;}public void Display(){Console.WriteLine("长度:{0}", length);Console.WriteLine("宽度:{0}", width);Console.WriteLine("面积:{0}", GetArea());}}class TableTop : Rectangle{private double price = 7;public TableTop(double l, double w) : base (l, w){}public double GetCost(){double cost;cost = GetArea() * price;return cost;}public void Display(){base.Display();Console.WriteLine("成本:{0}", GetCost());}}class ExecuteRectangle{static void Main(string[] args){TableTop t = new TableTop(4, 5);t.Display();Console.ReadKey();}// 长度:4// 宽度:5// 面积:20// 成本:140}
}

16.2 接口多重继承

多重继承指的是一个类别可以同时从多于一个父类继承行为与特征的功能。与单一继承相对,单一继承指一个类别只可以继承自一个父类。

C# 不支持多重继承,但可以使用接口来实现多重继承。

 using System;namespace Application
{class Shape // 基类{protected double length;protected double width;public void SetLength(double l) {length = l;}public void SetWidth(double w) {width = w;}}public interface Area  // 接口{double GetArea();}class Rectangle : Shape, Area  // 派生类{public double GetArea(){return length * width;}public void Display(){Console.WriteLine("长度:{0}", length);Console.WriteLine("宽度:{0}", width);Console.WriteLine("面积:{0}", GetArea());}}class ExecuteRectangle{static void Main(string[] args){Rectangle t = new Rectangle();t.SetLength(4);t.SetWidth(5);t.Display();Console.ReadKey();}// 长度:4// 宽度:5// 面积:20}
}

16.3 抽象类与接口区别

  • 接口支持多继承,抽象类不能实现多继承
  • 接口只能定义抽象规则,抽象类既能定义规则,还能提供已实现的成员
  • 接口是一组行为规范,抽象类是一个不完全的类
  • 接口可以用于支持回调,抽象类不能实现回调,因为继承不支持
  • 接口只包含方法、属性、索引器、事件的签名,但不能定义字段和包含实现的方法,抽象类可以定义字段、属性、包含有实现的方法
  • 接口可以作用于值类型和引用类型,抽象类只能作用于引用类型。例如struct就可以继承接口,而不能继承类。
abstract class A  // 抽象类
{public int a = 0;abstract public void func();  // 抽象方法
}interface B  // 接口
{public int a;  // 不能给字段赋值,写成public int a = 0;public static int b = 0;public void func();  // 接口方法,继承接口的派生类必须实现此方法
}

17. 多态性

多态性是指同一个行为具有多个不同表现形式或形态的能力,在面向对象编程中表现为"一个接口,多个功能"。

多态性可以分为编译时多态或者运行时多态,编译时多态(也称静态多态性)是编译时发生的函数响应,C# 提供了两种技术来实现编译时多态,分为函数重载和运算符重载。而运行时多态(也称动态多态性)是运行时发生的函数响应。

多态就是同一个接口使用不同实例而执行不同的操作。

17.1 编译时多态

函数重载

在同一范围内对相同函数名有多个定义,可以是参数类型或参数个数的不同,返回值类型除外。

using System;
namespace Application
{public class ReloadTest{public int Add(int a, int b, int c){return a + b + c;}public int Add(int a, int b){return a + b;}void Print(int i){Console.WriteLine("输出整型:{0}", i);}void Print(double f){Console.WriteLine("输出浮点型型:{0}", f);}}public class Test{static void Main(string[] args){ReloadTest data = new ReloadTest();int add1 = data.Add(1, 2);int add2 = data.Add(1, 2, 3);Console.WriteLine("add1 = {0}", add1);  // add1 = 3Console.WriteLine("add2 = {0}", add2);  // add2 = 6data.Print(1);  // 输出整型:1data.Print(1.23);  // 输出浮点型:1.23Console.ReadKey();}}
}

[运算符重载](#18. 运算符重载)

17.2 运行时多态

抽象类

C#允许使用关键字abstract创建抽象类,用于提供接口的部分类的实现,当一个派生类继承自该抽象类时,实现即完成。抽象类包含抽象方法,抽象方法可由派生类实现

注意:

  • 不能创建一个抽象类的实例
  • 不能在一个抽象类外部声明一个抽象方法
  • 通过关键字sealed可将类声明为密封类,该类不能被继承,抽象类不能被声明为密封类
using System;
namespace Application
{abstract class Shape // 抽象类{abstract public double GetArea();  // 抽象方法}class Rectangle : Shape  // 派生类{private double length;private double width;public Rectangle(double l = 0.0, double w = 0.0)  // 构造函数{length = l;width = w;}public override double GetArea()  // 重写抽象方法{return length * width;}}class Test{static void Main(string[] args){Rectangle r = new Rectangle(10.0, 7.0);double area = r.GetArea();Console.WriteLine("面积:{0}", area);  // 面积:70Console.ReadKey();}}
}

虚方法

当有一个定义在类中的方法需要在继承类中实现时,可以使用关键字virtual声明虚方法,虚方法可以在不同的基础类中有不同的实现。

using System;
namespace Application
{class Shape // 抽象类{protected double length, width;  // 成员变量public Shape(double l = 0.0, double w = 0.0)  // 构造函数{length = l;width = w;}public virtual double GetArea() { return 0; }  // 纯虚方法}class Rectangle : Shape  // 派生类{public Rectangle(double l = 0, double w = 0.0) : base(l, w){}public override double GetArea()  // 具体实现{Console.Write("Rectangle类的");return width * length;}}class Triangle : Shape  // 派生类{public Triangle(double l = 0, double w = 0.0) : base(l, w){}public override double GetArea()  // 具体实现{Console.Write("Triangle类的");return width * length / 2;}}class Caller{public void CallArea(Shape s){double area = s.GetArea();Console.WriteLine("面积:{0}", area);}}class Test{static void Main(string[] args){Caller c = new Caller();Rectangle r = new Rectangle(2, 10);Triangle t = new Triangle(10, 5);c.CallArea(r);  // Rectangle类的面积:20c.CallArea(t);  // Triangle类的面积:25Console.ReadKey();}}
}

18. 运算符重载

18.1 定义

C#内置的运算符可以被重定义或重载,因此可以使用用户自定义类型的运算符。重载运算符是具有特殊名称的函数,是通过关键字operator后跟运算符符号来定义的,其余跟其他函数一样,有返回类型和参数列表。如定义一个实现Box类加法运算符重载方法,实现两个Box对象对应属性相加,并返回。

public static Box operator+ (Box b, Box c)
{Box box = new Box();box.length = b.length + c.length;box.width = b.width + c.width;box.height = b.height + c.height;return box
}

18.2 实现

using System;namespace Application
{class Box{private double length;  // 长度private double width;  // 宽度private double height;  // 高度public double GetVolume(){return length * width * height;}public void SetLength(double l){length = l;}public void SetWidth(double w){width = w;}public void SetHeight(double h){height = h;}// 重载 + 运算符来把两个 Box 对象相加public static Box operator+ (Box b, Box c){Box box = new Box();box.length = b.length + c.length;box.width = b.width + c.width;box.height = b.height + c.height;return box;}}class Test{static void Main(string[] args){Box Box1 = new Box();         // 声明 Box1,类型为 BoxBox Box2 = new Box();         // 声明 Box2,类型为 BoxBox Box3 = new Box();         // 声明 Box3,类型为 Boxdouble volume = 0.0;          // 体积// Box1 详述Box1.SetLength(6.0);Box1.SetWidth(7.0);Box1.SetHeight(5.0);// Box2 详述Box2.SetLength(12.0);Box2.SetWidth(13.0);Box2.SetHeight(10.0);// Box1 的体积volume = Box1.GetVolume();Console.WriteLine("Box1 的体积:{0}", volume);  // Box1的体积:210// Box2 的体积volume = Box2.GetVolume();Console.WriteLine("Box2 的体积:{0}", volume);  // Box2的体积:1560// 把两个对象相加Box3 = Box1 + Box2;// Box3 的体积volume = Box3.GetVolume();Console.WriteLine("Box3 的体积:{0}", volume);  // Box3的体积:5400Console.ReadKey();}}
}

18.3 可重载与不可重载运算符

运算符 描述
+, -, !, ~, ++, -- 一元运算符只有一个操作数,且可以被重载
+, -, *, /, % 二元运算符带有两个操作数,且可以被重载
==, !=, <, >, <=, >= 比较运算符可以被重载
&&, `
+=, -=, *=, /=, %= 赋值运算符不能被重载
=, ., ?:, ->, new, is, sizeof, typeof 这些运算符不能被重载

19. 文件读写

文件是存储在磁盘中的具有特定名称和目录路径的数据集合,当使用程序对文件进行读写时,程序会将文件以数据流的形式读入内存中。因此流可以看作是通过通信路径传递的字节序列。

流分为输入流和输出流,输入流主要用来从文件读取数据,输出流主要用于向文件写入数据。

19.1 I/O类

System.IO命名空间中包含了各种作用于文件操作的类,例如文件创建、删除、读取、写入等。

I/O类 描述 I/O类 描述
BinaryReader 从二进制流中读取原始数据 BinaryWriter 以二进制格式写入原始数据
BufferedStream 临时存储字节流 Directory 对目录进行复制、移动、重命名、创建和删除等
DirectoryInfo 用于对目录执行操作 DriveInfo 获取驱动器信息
File 对文件进行操作 FileInfo 用于对文件执行操作
FileStream 用于文件中任何位置的读写 MomoryStream 用于随机访问存储在内存中的数据流
Path 对路径信息执行操作 StreamReader 用于从字节流中读取字符
StringReader 用于从字符串缓冲区读取数据 StringWriter 用于向字符串缓冲区写入数据

19.2 FileStream类

FileStream类在System.IO命名空间下,使用它可读取、写入和关闭文件。创建FileStream类对象的语法格式如下:

FileStream <object_name> = new FileStram(<file_name>, <FileMode Enumerator>, <FileAccess Enumerator>, <FileShare Enumerator>);

FileStream类中常用方法如下:

方法 描述 方法 描述
Close() 释放内存,关闭文件 CopyTo(Stream) 当前流读取字节并写入另一个流
Dispose() 是否流使用的所有资源 Equals(Object) 判断指定对象是否等于当前对象
Finalize() 确保资源回收和清理完成 Flush() 清除流的缓冲区,所有数据写入文件
GetHashCode() 获取哈希函数 GetType() 获取当前实例的Type
Lock(Int64, Int64) 防止其他进程读取或写入FileStream Read(Byte[], Int32, Int32) 读取字节块并写入缓冲区
ReadByte() 读取单个字节 ToString() 返回当前对象的字符串
Unlock(Int64, Int64) 允许其他进程访问FileStream Write(Byte[], Int32, Int32) 将字节块写入文件流
WriteByte(Byte) 写入单个字节到文件流当前位置
using System;
using System.IO;namespace Application
{class Test{static void Main(string[] args){FileStream file = new FileStream("test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite);  // 定义文件流for  (int i = 0; i < 20; i++){file.WriteByte((byte)i);  // 写入字节}file.Position = 0;  // 将读取位置重设为文件开头for (int i = 0; i < 20; i++){Console.Write(file.ReadByte() + " ");  // 读取字节}// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 file.Close();  // 关闭文件流Console.ReadKey();}}
}

19.3 高级文件操作

其他高级的I/O文件操作,如文本文件的读写,二进制文件的读写,Windows文件系统的操作等详见官方文档。

20. 预处理器

预处理器指令指导编译器在实际编译开始之前对信息进行预处理。

所有的预处理器指令都是以#开始的,一个预处理器指令必须是该行上唯一的指令。

预处理器指令 描述 预处理器指令 描述
#define 定义一系列称为符号的字符 #undef 取消定义的字符
#if 测试符号是否为真 #else 创建符合条件指令,与#if一起使用
#elif 创建符合条件指令,与#if一起使用 #endif 指定一个条件指令的结束
#line 可修改编辑器的行数以及删除错误和警告的文件名 #error 从代码指定位置生成一个错误
#warning 从代码指定位置生成一个警告 #region 指定可折叠代码块的起始
#endregion #region块的结束 #pragma 抑制或还原指定的编译警告
#define DEBUG
#define RELEASE
#pragma warning disable 168 // 取消编号168的警告
#pragma warning restore 168 // 还原编号168的警告
using System;
public class TestClass
{public static void Main(){#if (DEBUG && !RELEASE)Console.WriteLine("DEBUG is defined");#elif (!DEBUG && RELEASE)Console.WriteLine("RELEASE is defined");#elif (DEBUG && RELEASE)Console.WriteLine("DEBUG and RELEASE are defined");  // DEBUG and RELEASE are defined#else Console.WriteLine("DEBUG and RELEASE are not defined");#endifConsole.ReadKey();}
}

21. 异常处理

异常是在程序执行期间出现的问题,C#中的异常是对程序运行时出现的特殊情况的一种响应,比如尝试除以零。

异常提供了一种把程序控制权从某个部分转移到另一个部分的方式。

21.1 语法

C#异常处理是建立在trycatchfinallythrow四个关键词之上的。

  • try:一个try块标识了一个将被激活的特定的异常代码块,后跟一个或多个catch
  • catch:程序通过异常处理程序捕获异常,catch关键字表示异常的捕获
  • finally:用于执行给定的语句,不管异常是否被抛出都会执行
  • throw:当出现问题时,程序抛出一个异常,使用该关键字来完成
try
{// 可能引起异常的语句
}
catch (ExceptionName e1)
{// 错误处理代码
}
catch (ExceptionName e2)
{// 错误处理代码
}
finally
{// 无论是否抛出异常,都会执行的语句
}

21.2 异常类

C#异常是用类来表示的,异常类主要是直接或间接派生自System.Exception类。System.ApplicationException类支持由应用程序生成的异常,自定义类都应派生自该类。

System.SystemException类是所有预定义的系统异常的基类。

异常类 描述 异常类 描述
System.IO.IOException 处理I/O错误 System.IndexOutOfRangeException 处理数组索引超范围错误
System.ArrayTypeMismatchException 处理数组类型不匹配错误 System.NullReferenceException 处理当依次空对象时错误
System.DivideByZeroException 处理当除以零时错误 System.InvalidCastException 处理类型转换期间错误
System.OutOfMemoryException 处理空间内存不足错误 System.StackOverflowException 处理栈溢出错误
using System;
namespace ErrorHandleApplication
{class DivNumbers{int result;DivNumbers(){result = 0;}public void division(int num1, int num2){try{result = num1 / num2;}catch (DivideByZeroException e){Console.WriteLine("Exception caugth: {0}", e);}finally{Console.WriteLine("Result: {0}", result);}}static void Main(string[] args){DivNumbers d = new DivNumbers();d.division(10, 0);Console.ReadKey();}/* Exception caugth: System.DivideByZeroException: Attempted to divide by zero.at ErrorHandleApplication.DivNumbers.division (System.Int32 num1, System.Int32 num2) [0x00000] in <c41f6dc696cc46ac9b099084f035765f>:0
Result: 0
*/}
}

21.3 自定义异常

用户可以创建自定义异常,派生自ApplicationException类。

using System;// 自定义异常
public class TempIsZeroException : ApplicationException
{public TempIsZeroException(string message) : base (message){}
}public class Temperature
{int temperature = 0;public void ShowTemp(){if (temperature == 0){// 抛出异常throw(new TempIsZeroException("Zero temperature"));}else{Console.WriteLine("Temperature: {0}", temperature);}}
}namespace UserDefineExecption
{class TempTest{static void Main(string[] arg){Temperature temp = new Temperature();try{temp.ShowTemp();}catch (TempIsZeroException e){Console.WriteLine("TempIsZeroException: {0}", e.Message);// TempIsZeroException:Zero temperature}Console.ReadKey();}}
}

参考文章

本文是笔者通过下列网站教程学习C#的记录,有部分修改和补充,转载请注明出处,并附带下面链接。

1.【菜鸟教程C#教程】

2.【C语言中文网C#教程】

3.【微软C#官方文档】

C#语言基础学习笔记相关推荐

  1. 《C语言基础学习笔记》—— 黑马程序员 <Part1-4>

    <C语言基础学习笔记 Part1-4> Part 1 基础介绍 1. 第一个C语言程序:HelloWorld 1.1 编写C语言代码:hello.c 1.2代码分析 1)include 头 ...

  2. go语言基础学习笔记完整版

    目录 背景 基础 helloworld 变量 常量 数据类型 基本数据类型与复杂数据类型 值类型与引用类型 查看变量类型 字符与字符串 类型转换 指针 打包 读取控制台数据 for-range遍历 生 ...

  3. 【C语言基础学习笔记】三、函数(1)

    我走了很远的路,吃了很多的苦,才将这份博士学位论文送到你的面前.二十二在求学路,一路风雨泥泞,许多不容易.如梦一场-这一路,信念很简单,把书念下去,然后走出去,不枉活一世-理想不伟大,只愿年过半百,归 ...

  4. Go语言基础学习笔记

    Golang官方文档: 点击进入 尚硅谷视频:点击进入 目录 01.标识符 1.1.命名规则 1.2.标识符命名规范 1.3.go运算符优先级 02.数据类型 2.1.Go中数据类型分类 变量与常量 ...

  5. Java语言基础学习笔记——基础语法

    一.标识符 1:取名必须以_,$或者字母开头 2:变量必须先声明,赋值后才能使用 3:内存管理 4部分:1) code segment 存放代码                             ...

  6. 夜光:Java语言基础学习笔记(三)在校资源

    夜光序言: 青青子衿,悠悠我心. 纵我不往,子宁不嗣音? 青青子佩,悠悠我思. 纵我不往,子宁不来? 挑兮达兮,在城阙兮. 一日不见,如三月兮. 正文:流程控制语句 结构化程序的三种结构 顺序结构 选 ...

  7. c语言基础学习笔记(三):条件判断语句if-else嵌套和switch-case语句

    文章目录 if判断(条件判断) if语句 优先级 else语句 超市找零代码示例 两数比大小程序示例 if语句没有大括号 计算薪水程序示例 判断成绩及格示例 if-else嵌套 三个数比大小 级联的i ...

  8. R语言小白学习笔记12—概率分布

    R语言小白学习笔记12-概率分布 笔记链接 学习笔记12-概率分布 12.1 正态分布 12.2 二项分布 12.3 泊松分布 12.4 其他分布 笔记链接 学习笔记1-R语言基础. 学习笔记2-高级 ...

  9. R语言小白学习笔记13—基本统计

    R语言小白学习笔记13-基本统计 笔记链接 学习笔记13-基本统计 13.1 概括性统计量 13.2 相关系数和协方差 13.3 t-检验 13.3.1 单样本t检验 13.3.2 两样本t检验 13 ...

最新文章

  1. 命令行下编译Wordcount
  2. jquery如何对多个对象绑定同一事件
  3. Windows Server 2012 服务器之Web服务器
  4. com.android.ddmlib.ShellCommandUnresponsiveException
  5. 4.3.6 无分类编址CIDR(构成超网)
  6. 数据框筛选特定的子集
  7. android 消除标题,Android Activity 去掉标题栏及全屏显示
  8. asposeword.dll通过word模板生成word、PDF
  9. linux 修改文件名_Linux常用命令
  10. 怎样保护计算机连接线,一根网线把电脑烧了:雷雨天如何保护家电?
  11. 北大青鸟s2结业考试机试_重庆北大青鸟「学员心声」任何一次的考试,都是一次珍贵的蜕变...
  12. 编码规范二 缩进与注释
  13. C++产生指定范围内的随机数/随机小数
  14. 如何录制电脑内部声音
  15. 字符串系列——uva10361 - 自动作诗机
  16. Centos7修改服务器密码
  17. 怎么更新计算机显卡,显卡驱动怎么更新
  18. C++ emplace_back用法介绍
  19. turtle画了一个皮卡丘
  20. Syste类 及常用方法

热门文章

  1. 从 xp_cmdshell 中使用 dtexec 运行SSIS包
  2. Python 中列表与元组的异同
  3. 【工具】NDM下载神器,可替代IDM
  4. DD 摆磁铁(计蒜客信息学8月普及组模拟赛)
  5. html网页的说课稿,网页设计之滚动字幕说课稿
  6. lightroom 闪退_【HTTP网球iOS】Lightroom解锁
  7. 苹果电脑视频声音提取的方法
  8. 车载导航产品的数字地图格式
  9. 微信分享网络图片到朋友圈和收藏不显示图片的问题
  10. 阿里云云效:代码提交使用