1 前言
从事 .net 开发两年了,针对基础知识做一个总结
1.1重要功能
- 布尔条件(Boolean Conditions)
- 自动垃圾回收(Automatic Garbage Collection)
- 标准库(Standard Library)
- 组件版本(Assembly Versioning)
- 属性(Properties)和事件(Events)
- 委托(Delegates)和事件管理(Events Management)
- 易于使用的泛型(Generics)
- 索引器(Indexers)
- 条件编译(Conditional Compilation)
- 简单的多线程(Multithreading)
- LINQ 和 Lambda 表达式
- 集成 Windows
- dotnet core 跨平台
1.2 C#环境
- .Net 框架(.net framework, Only windows)
- mono (linux上的移植)
- dotnet core (官方出品、跨平台)
组成:
- 公共语言运行库(Common Language Runtime - CLR)
- .Net 框架类库(.Net Framework Class Library)
- 公共语言规范(Common Language Specification)
- dotnet (.NETStandard )
- dotnet framework (.NETFramework 4.5.2)
- 通用类型系统(Common Type System)
- 元数据(Metadata)和组件(Assemblies)
- Windows 窗体(Windows Forms)
- ASP.Net 和 ASP.Net AJAX
- ADO.Net
- Windows 工作流基础(Windows Workflow Foundation - WF)
- Windows 显示基础(Windows Presentation Foundation)
- Windows 通信基础(Windows Communication Foundation - WCF)
- LINQ
如需了解每个组件的详细信息,请参阅微软(Microsoft)的文档。
IDE:
- visual studio(Windows)
- visual studio code + 插件(跨平台)
2 基础
2.1 程序结构
主要包含以下部分:
- 命名空间声明(Namespace declaration)
- 一个 class
- Class 方法
- Class 属性
- 一个 Main 方法
- 语句(Statements)& 表达式(Expressions)
- 注释
1 | using System; |
具体跟C++的结构相类似,只不过要注意一点,C#是一个完全的面相对象的语言。所有的东西都被封装在命名空间和类中。
每一个命名空间以及类可以包含很多类以及方法、属性。
具体内容有什么区别,还是要靠自己学习的。
编译、运行
IDE 嘛,很简单的啦,点点几个鼠标就可以了啊
不过要注意的是,VS提供了多种不同的应用内容程序,(如WFC,Windows 通用,web 等)选择的时候要注意。反正就是可以干很多事情。
高级的编译选项可以通过msbuild编译,可以达到一些特殊的功能!如CI等
2.2 基础语法
- using 关键字
可以多次使用类似于#include
- class 关键字
和其他面向语言没有区别
- 注释
/* */ 或者 //
- 成员函数
实例化对象
1
ClassName objectName = new ClassName()
标识符
- 标识符必须以字母开头,后面可以跟一系列的字母、数字( 0 - 9 )或下划线( _ )。标识符中的第一个字符不能是数字。
- 标识符必须不包含任何嵌入的空格或符号,比如 ? - +! @ # % ^ & * ( ) [ ] { } . ; : “ ‘ / \。但是,可以使用下划线( _ )。
- 标识符不能是 C# 关键字。
- C#关键字
保留关键字 | 1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|---|
abstract | as | base | bool | break | byte | case | |
catch | char | checked | class | const | continue | decimal | |
default | delegate | do | double | else | enum | event | |
explicit | extern | false | finally | fixed | float | for | |
foreach | goto | if | implicit | in | in (generic modifier) | int | |
interface | internal | is | lock | long | namespace | new | |
null | object | operator | out | out(generic modifier) | override | params | |
private | protected | public | readonly | ref | return | sbyte | |
sealed | short | sizeof | stackalloc | static | string | struct | |
switch | this | throw | true | try | typeof | uint | |
ulong | unchecked | unsafe | ushort | using | virtual | void | |
volatile | while | ||||||
上下文关键字 | |||||||
add | alias | ascending | descending | dynamic | from | get | |
global | group | into | join | let | orderby | partial(type) | |
partial(method) | remove | select | set |
2.2数据类型
分为以下几种类型
- 值类型(Value types)
- 引用类型(Reference types)
- 指针类型(Pointer types)
值类型
从类System.ValueType中派生
值类型直接包含数据
类型 | 描述 | 范围 | 默认值 |
---|---|---|---|
bool | 布尔值 | True 或 False | False |
byte | 8 位无符号整数 | 0 到 255 | 0 |
char | 16 位 Unicode 字符 | U +0000 到 U +ffff | ‘\0’ |
decimal | 128 位精确的十进制值,28-29 有效位数 | (-7.9 x 10^28 到 7.9 x 10^28) / 10^0 ~ 10^28 | 0.0M |
double | 64 位双精度浮点型 | (+/-)5.0 x 10^-324 到 (+/-)1.7 x 10^308 | 0.0D |
float | 32 位单精度浮点型 | -3.4 x 1038 到 + 3.4 x 1038 | 0.0F |
int | 32 位有符号整数类型 | -2,147,483,648 到 2,147,483,647 | 0 |
long | 64 位有符号整数类型 | -923,372,036,854,775,808 到 9,223,372,036,854,775,807 | 0L |
sbyte | 8 位有符号整数类型 | -128 到 127 | 0 |
short | 16 位有符号整数类型 | -32,768 到 32,767 | 0 |
uint | 32 位无符号整数类型 | 0 到 4,294,967,295 | 0 |
ulong | 64 位无符号整数类型 | 0 到 18,446,744,073,709,551,615 | 0 |
ushort | 16 位无符号整数类型 | 0 到 65,535 | 0 |
1 | sizeof(int); |
引用类型(reference types)
不包含存储在变量中的实际数据,但能包含对对象的引用。
指的是一个内存位置,使用多个变量时,可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的 引用类型有:object、dynamic 和 string
对象(object)类型
对象(Object)类型 是 C# 通用类型系统(Common Type System - CTS)中所有数据类型的终极基类。Object 是 System.Object 类的别名。所以对象(Object)类型可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。
当一个值类型转换为对象类型时,则被称为 装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱。1
2object obj;
obj = 100;//装箱
动态(Dynamic)类型
可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。
声明动态类型的语法:1
dynamic <variable_name> = value;
例如:1
dynamic d = 20;
动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。
字符串(String)类型
字符串(String)类型 允许您给变量分配任何字符串值。字符串(String)类型是 System.String 类的别名。它是从对象(Object)类型派生的。字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。
例如:1
String str = "w3cschool.cc";
一个 @引号字符串:1
@"w3cschool.cc";
C# string 字符串的前面可以加 @(称作”逐字字符串”)将转义字符(\)当作普通字符对待,比如:1
string str = @"C:\Windows";
等价于:1
string str = "C:\\Windows";
@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。1
2
3
4string str = @"<script type=""text/javascript"">
<!--
-->
</script>";
用户自定义引用类型有:class、interface 或 delegate。我们将在以后的章节中讨论这些类型。
指针类型(Pointer types)
指针类型变量存储另一种类型的内存地址。C# 中的指针与 C 或 C++ 中的指针有相同的功能。
声明指针类型的语法:1
type* identifier;
例如:1
2char* cptr;
int* iptr;
不安全的代码
C#类型转换
类型转换从根本上说是类型铸造,或者说是把数据从一种类型转换为另一种类型。在 C# 中,类型铸造有两种形式:
- 隐式类型转换 - 这些转换是 C# 默认的以安全方式进行的转换。例如,从小的整数类型转换为大的整数类型,从派生类转换为基类。
- 显式类型转换 - 这些转换是通过用户使用预定义的函数显式完成的。显式转换需要强制转换运算符。
下面的实例显示了一个显式的类型转换:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15namespace TypeConversionApplication
{
class ExplicitConversion
{
static void Main(string[] args)
{
double d = 5673.74;
int i;
// 强制转换 double 为 int
i = (int)d;
Console.WriteLine(i);
Console.ReadKey();
}
}
}
C# 类型转换方法
序号 | 方法 & 描述 |
---|---|
1 | ToBoolean如果可能的话,把类型转换为布尔型。 |
2 | ToByte把类型转换为字节类型。 |
3 | ToChar如果可能的话,把类型转换为单个 Unicode 字符类型。 |
4 | ToDateTime把类型(整数或字符串类型)转换为 日期-时间 结构。 |
5 | ToDecimal把浮点型或整数类型转换为十进制类型。 |
6 | ToDouble把类型转换为双精度浮点型。 |
7 | ToInt16把类型转换为 16 位整数类型。 |
8 | ToInt32把类型转换为 32 位整数类型。 |
9 | ToInt64把类型转换为 64 位整数类型。 |
10 | ToSbyte把类型转换为有符号字节类型。 |
11 | ToSingle把类型转换为小浮点数类型。 |
12 | ToString把类型转换为字符串类型。 |
13 | ToType把类型转换为指定类型。 |
14 | ToUInt16把类型转换为 16 位无符号整数类型。 |
15 | ToUInt32把类型转换为 32 位无符号整数类型。 |
16 | ToUInt64把类型转换为 64 位无符号整数类型。 |
1 | namespace TypeConversionApplication |
C#变量
和C++ 相同,C# 允许定义其他值类型的变量,比如 enum,也允许定义引用类型变量,比如 class。这些我们将在以后的章节中进行讨论。1
2
3
4
5
6
7<data_type><variable_list>; //定义式
<data_type><variable_name> = value; //初始化
int num;
num = Convert.ToInt32(Console.ReadLine());
//用户输入
- lvalue:lvalue 表达式可以出现在赋值语句的左边或右边。
- rvalue:rvalue 表达式可以出现在赋值语句的右边,不能出现在赋值语句的左边。
C#常量
- 整数常量
整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,没有前缀则表示十进制。
整数常量也可以有后缀,可以是 U 和 L 的组合,其中,U 和 L 分别表示 unsigned 和 long。后缀可以是大写或者小写,多个后缀以任意顺序进行组合。
- 浮点常量
一个浮点常量是由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。
字符常量
字符串常量
- 定义常量
1
const <data_type><constant_name> = value;
C#运算符
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
杂项运算符
c = a++: 先将 a 赋值给 c,再对 a 进行自增运算。
- c = ++a: 先将 a 进行自增运算,再将 a 赋值给 c 。
- c = a–: 先将 a 赋值给 c,再对 a 进行自减运算。
- c = –a: 先将 a 进行自减运算,再将 a 赋值给 c 。
位运算符
同C语言的相同,&,|,^。
A = 0011 1100
B = 0000 1101
运算符 | 描述 | 实例 | ||
---|---|---|---|---|
& | 如果同时存在于两个操作数中,二进制 AND 运算符复制一位到结果中。 | (A & B) 将得到 12,即为 0000 1100 | ||
\ | 如果存在于任一操作数中,二进制 OR 运算符复制一位到结果中。 | (A \ | B) 将得到 61,即为 0011 1101 | |
^ | 如果存在于其中一个操作数中但不同时存在于两个操作数中,二进制异或运算符复制一位到结果中。 | (A ^ B) 将得到 49,即为 0011 0001 | ||
~ | 二进制补码运算符是一元运算符,具有”翻转”位效果。 | (~A ) 将得到 -61,即为 1100 0011,2 的补码形式,带符号的二进制数。 | ||
<< | 二进制左移运算符。左操作数的值向左移动右操作数指定的位数。 | A << 2 将得到 240,即为 1111 0000 | ||
>> | 二进制右移运算符。左操作数的值向右移动右操作数指定的位数。 | A >> 2 将得到 15,即为 0000 1111 |
reference:
http://www.runoob.com/csharp/csharp-operators.html
C#判断
跟其C类似
exp(判断) ? exp2:exp3
C#循环
跟其C类似
C# 封装
封装 被定义为”把一个或多个项目封闭在一个物理的或者逻辑的包中”。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。
访问修饰符
- Public
- Private
- Protected
- Internal
- Protected internal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34using System;
namespace RectangleApplcation
{
class Rectangle
{
public double length;
public double width;
public double GetArea()
{
return length * width;
}
public void Display()
{
Console.WriteLine("length: {0}",length);
Console.WriteLine("width: {0}",width);
Console.WriteLine("Area: {0}",GetArea);
}
}
class ExecuteRectangle
{
static void Main(string[] args)
{
Rectangle r = new Rectangle();
r.leength = 4.5;
r.width = 3.5;
r.Display();
Console.ReadLine();
}
}
}
Internal访问修饰符
Internal访问说明符 允许一个类将其成员变量和成员函数暴露给其它函数和对象。
带有 internal 访问修饰符的任何成员可以被定义在该成员所定义的++应用程序++内的任何类或方法访问。
Protected Internal 访问修饰符
Protected Internal 访问修饰符允许一个类将其成员变量和成员函数对同一应用程序内的子类以外的其他的类对象和函数进行隐藏。这也被用于实现继承。
C#方法
定义方法
1 | <Access Specofier><Return Type><Method Name>(parameter List) |
- Access Specifier: 访问修饰符,决定了变量或方法对另一个类的可见性
- Return type:返回类型
- Method name: 方法名称 大小写敏感
- Parameter list:参数列表
- Method body: 方法主体
调用方法
使用方式名调用方法,跟其他语言差不多,要先实例化类,再调用方法
递归方法调用
一个方法可以自我调用,称之为递归。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30using System;
namespace CalculatorApplication
{
calss NumberManipulator
{
public int factorial(int num)
{
int result;
if (num == 1)
return 1;
else
{
result = factorial(num - 1) * num;
return result;
}
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
Console.WriteLine("6的阶乘是: {0}",n.factorial(6));
Console.WriteLine("7的阶乘是: {0}", n.factorial(7));
Console.WriteLine("8的阶乘是: {0}",n.factorical(8));
Console.ReadLine();
}
}
}
参数传递
方式 | 描述 |
---|---|
值参数 | 这种方式复制参数的实际值给函数的形式参数,实参和形参使用的是两个不同内存中的值。在这种情况下,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。 |
引用参数 | 这种方式复制参数的内存位置的引用给形式参数。这意味着,当形参的值发生改变时,同时也改变实参的值。 |
输出参数 | 这种方式可以返回多个值。 |
按值传递参数
这是参数传递的默认方式。在这种方式下,当调用一个方法时,会为每个值参数创建一个新的存储位置。
实际参数的值会复制给形参,实参和形参使用的是两个不同内存中的值。所以,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。1
2
3
4
5
6
7
8
9
10
11void swap(int x,int y)
{
int temp;
temp = x;
x = y;
y = temp;
}
//在主函数中调用这个函数不会改变两个数的值。
按引用传递参数
引用参数是对一个变量的内存位置的引用。它不会为这些参数创建一个新的存储位置。引用参数表示与提供给方法的实际参数具有相同的内存位置。
在 C# 中,使用 ref 关键字声明引用参数。
1 | void swap (ref int x, ref int y){} |
按输出传递参数
return 语句可用于从函数中返回一个值。可以使用输出参数来从函数中返回两个值。输出参数会把方法输出的数据赋给自己,其他方面与引用参数相似。
此时的参数前面需要加上一个关键字out。
1
2
3
4
5
6
7
8
9
10
11public void getValues(out int x, out int y)
{
x = //
y = //
}
classname n = new classname();
int a, b;
n.getValues(out a, out b);
当调用getValues(out a, out b)函数时,会把a和b的值在函数中运算之后的结果返回来
C#可空类型(Nullable)
可空类型可以表示其基础值类型正常范围内的值,再加上一个 null 值。
例如,Nullable< Int32 >,读作”可空的 Int32”,可以被赋值为 -2,147,483,648 到 2,147,483,647 之间的任意值,也可以被赋值为 null 值。类似的,Nullable< bool > 变量可以被赋值为 true 或 false 或 null。
声明一个nullable类型的语法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//<data_type>?<variable_name> = null;
using System;
namespace CalulatorApplication
{
class NullablesAtShow
{
int? num1 = null;
int? num2 = 45;
double num3 = new double?();
double num4 = 3.14157;
bool?boolval = new bool?();
Console.WriteLine("显示可空类型的值: {0}, {1}, {2}, {3}",
num1, num2, num3, num4);
Console.WriteLine("一个可空的布尔值: {0}", boolval);
Console.ReadLine();
}
}
Null 合并运算符( ?? )
Null 合并运算符用于定义可空类型和引用类型的默认值。Null 合并运算符为类型转换定义了一个预设值,以防可空类型的值为 Null。Null 合并运算符把操作数类型隐式转换为另一个可空(或不可空)的值类型的操作数的类型。
如果第一个操作数的值为 null,则运算符返回第二个操作数的值,否则返回第一个操作数的值。1
2
3
4
5
6
7
8
9
10
11double? num1 = null;
double? num2 = 3.14157;
double num3;
num3 = num1 ?? 5.34;
Console.WriteLine("num3 的值: {0}", num3);
// 5.34
num3 = num2 ?? 5.34;
Console.WriteLine("num3 的值: {0}", num3);
// 3.14157
Console.ReadLine();
C#数组(Array)
声明数组
1 | datatype[] arrayName; |
- datatype 元素类型
- [] 数组的秩(维度)。 数组的大小
- arrayname 数组名称
初始化数组
数组是一个引用类型,所以您需要使用 new 关键字来创建数组的的实例。1
2
3
4
5
6
7
8//单独赋值给单独元素
double[] balance = new double[10];
balance[0] = 4500.0;
//略,其他类似
//
//
//
1 | //C# 多维数组 |
可用一个foreach语句来历遍数组1
2
3
4foreach (var item in collection)
{
}
二维数组
1 | string[,] names; |
交错数组
1 | //交错数组是数组的数组。您可以声明一个带有 int 值的交错数组 scores,如下所示: |
还可对用数组当函数参数, 参数数组等
C#的array类
C#结构(struct)
在结构中,元素需要增加限制符1
2
3
4
5
6
7struct Books
{
public string title;
public string author;
public string subject;
public int bood_id;
};
特点:
- 结构可带有方法、字段、索引、属性、运算符方法和事件。
- 结构可定义构造函数,但不能定义析构函数。但是,您不能为结构定义默认的构造函数。默认的构造函数是自动定义的,且不能被改变。
- 与类不同,结构不能继承其他的结构或类。
- 结构不能作为其他结构或类的基础结构。
- 结构可实现一个或多个接口。
结构成员不能指定为 abstract、virtual 或 protected。
当您使用 New 操作符创建一个结构对象时,会调用适当的构造函数来创建结构。与类不同,结构可以不使用 New 操作符即可被实例化。
如果不使用 New 操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用。
C#枚举(Enum)
C#类(Class)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<access specifier> class class_name
{
// member variables
<access specifier> <data type> variable1;
<access specifier> <data type> variable2;
...
<access specifier> <data type> variableN;
// member methods
<access specifier> <return type> method1(parameter_list)
{
// method body
}
<access specifier> <return type> method2(parameter_list)
{
// method body
}
...
<access specifier> <return type> methodN(parameter_list)
{
// method body
}
}访问标识符
指定了对类及其成员的访问规则。如果没有指定,则使用默认的访问标识符。类的默认访问标识符是 internal,成员的默认访问标识符是 private。 - 数据类型 指定了变量的类型,返回类型
指定了返回的方法返回的数据类型。 - 如果要访问类的成员,您要使用点(.)运算符。
- 点运算符链接了对象的名称和成员的名称。
成员函数和封装
跟Java/C++ 差不多。
构造函数、析构函数
跟C++中的也差不多。
同样有构造函数,默认构造函数(不带参数)。
参数化构造函数。可以在创建对象的同时给对象赋初始值。
析构函数同C++也很类似。1
2
3
4
5ClassName objectName = new ClassName(arguments)
Line line = new Line(10.0)
~ClassName();//析构函数
C#类的静态成员
我们可以使用 static 关键字把类成员定义为静态的。当我们声明一个类成员为静态时,意味着无论有多少个类的对象被创建,只会有一个该静态成员的副本。
关键字 static 意味着类中只有一个该成员的实例。静态变量用于定义常量,因为它们的值可以通过直接调用类而不需要创建类的实例来获取。静态变量可在成员函数或类的定义外部进行初始化。您也可以在类的定义内部初始化静态变量。
这点跟C++的一样,全局静态变量。下面的例子能加深印象。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36using System;
namespace StaticVarApplication
{
class StaticVar
{
public static int num;
public void count()
{
num++;
}
public int getNum()
{
return num;
}
}
class StaticTester
{
static void Main(string[] args)
{
StaticVar s1 = new StaticVar();
StaticVar s2 = new StaticVar();
s1.count();
s1.count();
s1.count();
s2.count();
s2.count();
s2.count();
Console.WriteLine("s1 的变量 num: {0}", s1.getNum());
Console.WriteLine("s2 的变量 num: {0}", s2.getNum());
Console.ReadKey();
}
}
}
//result:
//s1 的变量 num: 6
//s2 的变量 num: 6
当前面无修饰符时,类的默认访问符为internal,成员的默认访问标识符为private,方法的访问符也是private。
C#继承
基类(父类),派生类(子类)
继承的思想实现了 属于(IS-A) 关系。例如,哺乳动物 属于(IS-A) 动物,狗 属于(IS-A) 哺乳动物,因此狗 属于(IS-A) 动物。
创建语法
1 | <access-specifiter> class <base_class> |
基类的初始化
派生类继承了基类的成员变量以及成员方法,因此父类在子类对象创建之前就应该被创建。如同C++一样
可以在成员初始化列表中进行父类的初始化。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52using System;
namespace RectangleApplication
{
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());
}
}//end class Rectangle
class Tabletop : Rectangle
{//类继承
private double cost;
public Tabletop(double l, double w) : base(l, w)
//初始化基类
{ }
public double GetCost()
{
double cost;
cost = GetArea() * 70;
return cost;
}
public void Display()
{
base.Display();
Console.WriteLine("成本: {0}", GetCost());
}
}
class ExecuteRectangle
{
static void Main(string[] args)
{
Tabletop t = new Tabletop(4.5, 7.5);
t.Display();
Console.ReadLine();
}
}
}
C#多重继承
C#不支持多重继承,可以使用接口来实现多重继承。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31class Shape
{
public void setWidth(int w)
{
width = w;
}
public void setHeight(int h)
{
height = h;
}
}
//基类 PaintCost
public interface PaintCost
{
int getCost(int area);
}
// 派生类
class Rectangle : Shape, PaintCost
{//此处为多重继承
public int getArea()
{
return (width * height);
}
public int getCost(int area)
{
return area * 70;
}
}
C#多态性
多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为”一个接口,多个功能”。
多态性可以是静态的或动态的。++在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。++
静态多态性
在编译时,函数的对象的连接机制称之为早期绑定,也称之为静态绑定。
- 函数重载
- 运算符重载
函数重载
在同一范围内对相同的函数名有多个定义。函数的定义必须彼此不同,可参数不同(位置,类型)。
不能重载的只有返回类型不同的函数声明。
跟C++中的还是差不了多少。类似于静态联编。
example 略、
动态多态性
C# 允许您使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。当一个派生类继承自该抽象类时,实现即完成。抽象类包含抽象方法,抽象方法可被派生类实现。
- 不能创建一个抽象类的实例。
- 不能再抽象类外部声明一个抽象函数。
- 在类定义前面防止关键字sealed,可以将类声明为密封类。当一个类被声明为sealed时,不能被继承。
抽象类不能声明为sealed。
例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36using System;
namespace PolymorphismApplication
{
abstract class Shape
{
public abstract int area();
}
class Rectangle: Shape
{
private int length;
private int width;
public Rectangle( int a=0, int b=0)
{
length = a;
width = b;
}
public override int area ()
{ //注意这里有override关键字,和C++不需要注意区别
Console.WriteLine("Rectangle 类的面积:");
return (width * length);
}
}
class RectangleTester
{
static void Main(string[] args)
{
Rectangle r = new Rectangle(10, 7);
double a = r.area();
Console.WriteLine("面积: {0}",a);
Console.ReadKey();
}
}
}
//Rectangle 类的面积:
//70
当有一个定义在类中的函数需要在继承类中实现时,可以使用 虚方法。 虚方法是使用关键字 virtual 声明的。 虚方法可以在不同的继承类中有不同的实现。对虚方法的调用是在运行时发生的。
++动态多态性是通过 抽象类 和 虚方法 实现的。++1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68using System;
namespace PolymorphismApplication
{
class Shape
{
protected int width, height;
public Shape( int a=0, int b=0)
{
width = a;
height = b;
}
public virtual int area()
{//注意在父类中方法有virtual 函数。
Console.WriteLine("父类的面积:");
return 0;
}
}
class Rectangle: Shape
{
public Rectangle( int a=0, int b=0): base(a, b)
{
}
public override int area ()
{
Console.WriteLine("Rectangle 类的面积:");
return (width * height);
}
}
class Triangle: Shape
{
public Triangle(int a = 0, int b = 0): base(a, b)
{
}
public override int area()
{
Console.WriteLine("Triangle 类的面积:");
return (width * height / 2);
}
}
class Caller
{
public void CallArea(Shape sh)
{
int a;
a = sh.area();
Console.WriteLine("面积: {0}", a);
}
}
class Tester
{
static void Main(string[] args)
{
Caller c = new Caller();
Rectangle r = new Rectangle(10, 7);
Triangle t = new Triangle(10, 5);
c.CallArea(r);
c.CallArea(t);
Console.ReadKey();
}
}
}
//Rectangle 类的面积为:
//70
//Triangle
//25
基类以及派生类,基础和C++差不多,当类函数重载时,要添加关键字override。其中,base关键字是继承使用基类中的属性1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60abstract class Shape
{//抽象类,类似于C++的中的抽象类
//是不能具体实例化的类
public const double pi = Math.PI;
protected double x,y;
public Shape(double x, double y)
{
this.x = x;
this.y = y;
}
public abstract double Area();
}
//abstract methods.
//注意在C++中并没有抽象函数,但是在C#中,抽象类以及抽象方法一定要一起出现。
//抽象类的派生类必须实现抽象方法体
class Circle : Shape
{
public Circle(double radius)
: base(radius, 0)
{
}
public override double Area()
{
return pi * x * x;
}
}
class Cylinder : Cricle
{
public Cylinder(double radius, double height)
: base(radius)
{
y = height;
}
public override double Area()
{
//在派生类中必须要把抽象方法实例化,此时要加override
return (2 * base.Area()) + ( 2 * pi * x * y);
}
}
class TestShape
{
static void Main()
{
double radius = 2.5;
double height = 3.0;
Cricle ring = new Cricle(radius);
Cylinder tube = new Cylinder(radius, height);
Console.WriteLine("Area of the circle = {0:F2}", ring.Area());
Console.WriteLine("Area of the cylinder = {0:F2}", tube.Area()
}
}
注意:
在C++中,带有纯虚函数的类称之为抽象类,不能被实例化。
基类的初始化
在派生类中的构造函数中初始化,跟C++差不多。
C#多重继承
不支持多重继承,可用接口的方式调用。
具体内容可见http://www.runoob.com/csharp/csharp-inheritance.html
Nullable
可空类型表示在基础值类型正常范围内的值,可以加上一个NULL值。
1 | 语法: <data_type>?<variable_name> = null; |
NULL合并运算符 (??)
Null 合并运算符为类型转换定义了一个预设值,以防可空类型的值为 Null。Null合并运算符把操作数类型隐式转换为另一个可空(或不可空)的值类型的操作数的类型。
如果第一个操作数的值为null,则运算符返回第二个操作数的值,否则返回第一个操作数的值1
2
3
4
5
6
7
8
9
10
11double? num1 = null;
double? num2 = 3.14157;
double num3;
num3 = num1 ?? 5.34;
Console.WriteLine("num3 的值: {0}", num3);
// 5.34
num3 = num2 ?? 5.34;
Console.WriteLine("num3 的值: {0}", num3);
// 3.14157
Console.ReadLine();
parmas 关键字
params 关键字,可以调用数组为形参的方法时,既可以传递数组实参,也可以只传递一组数组。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25public 返回类型 方法名称{parmas 类型名称[] 数组名称}
class ParamArray
{
public int AddElements(parmas int[] arr)
{
int sum = 0;
foreach(int i in arr)
{
sum += i;
}
return sum;
}
}
class TestClass
{
static void Main(string[] args)
{
ParamArray app = new ParamArray();
int sum = app.AddElements(512, 720, 250, 567, 889);
Console.WriteLine("总和是: {0}",sum)
Console.ReadKey();
}
}
- 通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。
C#接口
接口定义了所有类继承接口时应遵循的愈发合同。
定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。
抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时。1
2
3
4
5
6
7接口描述的是行为,而继承表示属于(is-a)
public interface ITransactions
{
void showTransaction();
double getAmount();
}
\\实现是在子类中实现的
命名空间
和其他语言差不多,调用空间中的成员时会吧名称置于前面,可以嵌套。1
namespace_name.item_name;
using关键字
和c++类似。
C#异常处理
- try:一个 try 块标识了一个将被激活的特定的异常的代码块。后跟一个或多个 catch 块。
- catch:程序通过异常处理程序捕获异常。catch 关键字表示异常的捕获。
- finally:finally 块用于执行给定的语句,不管异常是否被抛出都会执行。例如,如果您打开一个文件,不管是否出现异常文件都要被关闭。
- throw:当问题出现时,程序抛出一个异常。使用 throw 关键字来完成。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20try
{
// 引起异常的语句
}
catch( ExceptionName e1 )
{
// 错误处理代码
}
catch( ExceptionName e2 )
{
// 错误处理代码
}
catch( ExceptionName eN )
{
// 错误处理代码
}
finally
{
// 要执行的语句
}
C#中的异常类
C# 中的异常类主要是直接或间接地派生于 System.Exception 类。System.ApplicationException 和 System.SystemException 类是派生于 System.Exception 类的异常类。
异常类|描述
–|–
System.IO.IOException |处理 I/O 错误。
System.IndexOutOfRangeException |处理当方法指向超出范围的数组索引时生成的错误。
System.ArrayTypeMismatchException |处理当数组类型不匹配时生成的错误。
System.NullReferenceException |处理当依从一个空对象时生成的错误。
System.DivideByZeroException |处理当除以零时生成的错误。
System.InvalidCastException |处理在类型转换期间生成的错误。
System.OutOfMemoryException |处理空闲内存不足生成的错误。
System.StackOverflowException |处理栈溢出生成的错误。
##### 抛出对象1
2
3
4
5
6如果异常时直接或者间接派生自System.Exception类,可以抛出一个对象。
catch (exception e)
{
···
Throw e
}
C#文件输入输出
数据流 是通过通信路径传递的字节序列。
System.IO命名空间中常用的非抽象类:
IO类|描述
–|–
BinaryReader |从二进制流读取原始数据。
BinaryWriter |以二进制格式写入原始数据。
BufferedStream |字节流的临时存储。
Directory |有助于操作目录结构。
DirectoryInfo |用于对目录执行操作。
DriveInfo |提供驱动器的信息。
File |有助于处理文件。
FileInfo |用于对文件执行操作。
FileStream |用于文件中任何位置的读写。
MemoryStream |用于随机访问存储在内存中的数据流。
Path |对路径信息执行操作。
StreamReader |用于从字节流中读取字符。
StreamWriter |用于向一个流中写入字符。
StringReader |用于读取字符串缓冲区。
StringWriter |用于写入字符串缓冲区。
装箱和拆箱(box unbox)
针对基本类型, 因为是在stack中,要移动到托管堆中就要使用box方法。即把值类型转化为引用类型。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var list = new ArrayList();
list.Add(44); // boxing — convert a value type to a reference type
int i1 = (int)list[0]; // unboxing — convert a reference type to
// a value type
foreach (int i2 in list){
Console.WriteLine(i2);
}
//generics high performance
var list = new List<int>();
list.Add(44); // no boxing — value types are stored in the List<int>
int i1 = list[0]; // no unboxing, no cast needed
foreach (int i2 in list)
{
Console.WriteLine(i2);
}