快速上手CS

快速上手CS

常用关键字

abstract

可以和类、方法、属性、索引器及事件一起使用(成员变量、委托不能使用)

类声明中使用 abstract 修饰符以指示类只能是其他类的基类

方法或属性声明中使用 abstract 修饰符以指示此方法或属性不包含实现

抽象类特征

1、抽象类不能实例化

2、抽象类可以包含抽象方法和抽象访问器

3、不能用 sealed 修饰符修改抽象类,这意味着该类不能被继承

4、从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实现

5、在抽象类中可以定义静态方法

抽象方法特点

1、抽象方法是隐式的 virtual 方法

2、只允许在抽象类中使用抽象方法声明

3、因为抽象方法声明不提供实现,所以没有方法体

4、实现由override方法提供,它是非抽象类的成员

5、在抽象方法声明中使用 static 或 virtual 修饰符是错误的

抽象属性特点

1、在静态属性上使用 abstract 修饰符是错误的

2、在派生类中,通过包括使用 override 修饰符的属性声明可以重写抽象的继承属性

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
abstract class A
{
public int a;
public string[] arr;
public delegate void ptr();

public abstract int b{get;set;}
public abstract void method0();
public abstract event ptr evt;
public abstract string this[int index]{get;set;}

public static int c;
public static void method()
{
System.Console.WriteLine("method");
}
}

const

限定一个变量不允许被改变

1、用于修改字段或局部变量的声明,表示指定的字段或局部变量的值是常数不能被修改

2、常数声明的类型指定声明引入的成员类型。常数表达式必须产生具有目标类型或可隐式转换为目标类型的值

3、常数表达式是在编译时可被完全计算的表达式。因此不能从一个变量中提取值来初始化常量

4、常数声明可以声明多个常数

5、不允许在常数声明中使用static修饰符,因为const默认为static

6、常数可以参与常数表达式:const int c = 5; const int d = c + 10;

others

  • as: 安全的类型转换

  • is: 检查对象是否与给定的类型兼容

  • base:基类

  • byte: 一个字节

  • checked: 检查数值是否溢出

  • decimal: 128位数据类型,适用于财务和货币计算:decimal myMonney = 99.9m;

  • enum:枚举:enum Days{Sun, Mon, Tue};

  • explicit: 强制类型转换

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class A
    {
    ...
    }
    class B
    { //将A强制转换成B
    public static explicit operator B(A a)
    {
    return new B(a);
    }
    }
  • implicit: 隐式转换,方式同explicit

  • try/catch/throw/finally: 用于处理异常,也可以实现多层嵌套内直接跳转到最外层

  • fixed:放置GC重新定位可移动的变量

  • goto:直接跳转到对应标签位置

  • internal:声明的类型或者成员只能在同一程集内访问

  • lock:与线程有关

  • out:类似于ref,但只能用来将值从方法中传出

  • params:可变长参数,在声明方法时参数类型或者个数不确定时使用method(params Object[] objs)

  • readonly:动态常量,可在运行时赋值,可以被static修饰

  • sbyte: 有符号字节

  • sealed:被修饰的类或者方法不能被继承或者重写,密封方法必须对基类的虚方法进行重载,不能用来修饰成员变量

  • virtual: 用于修饰方法、属性、索引器或事件声明,并使它们可以在派生类中被重写

  • override:提供从基类继承的成员的新实现,被重写的属性必须是virtual, abstract 或者override的

    (函数和属性分为虚和非虚,只有虚方法才能被继承重写或者密封)

  • stackalloc: 在堆栈上分配内存块

  • struct: 结构体

  • uint, ulong, ushort: 无符号变量

  • volatile:字段可能被多个并发的执行线程修改

  • operator: 重载运算符

类、接口

定义类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A
{
public int a;
public A(int b=0) //默认构造函数
{
return;
}
public static void Main()
{
A a = new A(); //可以不用传递参数
}
~A()
{
//析构函数
}
}

类的继承:

1
2
3
4
5
6
7
class B: A
{
public B(int b):base(b) //初始化基类
{
base.a = b; //设置基类的成员
}
}

接口:

1
2
3
4
interface IMyInterface
{
void method();
}

接口支持多重继承,类不支持

文件处理

FileStream类

用于文件的读写和关闭

1
2
3
4
5
6
7
FileStream fs = new FileStream(filename, FileMode.Open | Append | Create, FileAccess.Read | Write);
fs.WriteByte(); //写入一个字节
fs.Write(array, offset, count); //array->file
fs.Read(array, offset, count); //file->array
fs.ReadByte(); //读一个字节
fs.Seek(offset, System.IO.SeekOrigin.Current); //移动文件指针
fs.Dispose(); //释放资源

StreamReader类

文本文件读取

1
2
3
StreamReader reader = new StreamReader(filename);
reader.Read();
reader.ReadLine();

StreamWriter类

文本文件写入

用法类似reader

命名空间

命名空间的设计目的是提供一种让一组名称与其他名称分隔开的方式。在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突。

常用命名空间:

1
2
3
4
using System;
using System.IO; //文件读写
using System.Collections; //集合
using System.Collections.Generic; //泛型数据结构

特性和反射

特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。

特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。

常用特性:

Conditional

这个预定义特性标记了一个条件方法,其执行依赖于指定的预处理标识符。

它会引起方法调用的条件编译,取决于指定的值,比如 DebugTrace。例如,当调试代码时显示变量的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define DEBUG
using System;
...
public class MyClass{
[Conditional("DEBUG")]
public static void method(string msg){
...
}
}
class Test{
public static void Main(){
Myclass.Message("DEBUG");
}
}

//当定义了debug时,输出DEBUG,否则不输出

Obsolete

这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。

1
2
3
4
[Obsolete(
message,
iserror
)]

其中:

  • 参数 message,是一个字符串,描述项目为什么过时以及该替代使用什么。
  • 参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;
public class MyClass
{
[Obsolete("Don't use OldMethod, use NewMethod instead", true)]
static void OldMethod()
{
Console.WriteLine("It is the old method");
}
static void NewMethod()
{
Console.WriteLine("It is the new method");
}
public static void Main()
{
OldMethod();
}
}

自定义特性和反射

反射指程序可以访问、检测和修改它本身状态或行为的一种能力。

程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。

您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

优缺点

优点:

  • 反射提高了程序的灵活性和扩展性。
  • 降低耦合性,提高自适应能力。
  • 它允许程序创建和控制任何类的对象,无需提前硬编码目标类

缺点:

  • 性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
  • 使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

用途

反射(Reflection)有下列用途:

  • 它允许在运行时查看特性(attribute)信息
  • 它允许审查集合中的各种类型,以及实例化这些类型。
  • 允许延迟绑定的方法和属性(property)。
  • 它允许在运行时创建新类型,然后使用这些类型执行一些任务。
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
using System;
using System.Reflection;//System.Reflection 类的 MemberInfo用于发现与类相关的特性(attribute)。
namespace BugFixApplication
{
// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage
#region//定义了特性能被放在那些前面
(AttributeTargets.Class |//规定了特性能被放在class的前面
AttributeTargets.Constructor |//规定了特性能被放在构造函数的前面
AttributeTargets.Field |//规定了特性能被放在域的前面
AttributeTargets.Method |//规定了特性能被放在方法的前面
AttributeTargets.Property,//规定了特性能被放在属性的前面
#endregion
AllowMultiple = true)]//这个属性标记了我们的定制特性能否被重复放置在同一个程序实体前多次。

public class DeBugInfo : System.Attribute//继承了预定义特性后的自定义特性
{
private int bugNo;
private string developer;
private string lastReview;
public string message;

public DeBugInfo(int bg,string dev,string d)//构造函数,接收三个参数并赋给对应实例
{
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}
#region//定义对应的调用,返回对应值value
public int BugNo
{
get
{
return bugNo;
}
}
public string Developer
{
get
{
return developer;
}
}
public string LastReview
{
get
{
return lastReview;
}
}
//前面有public string message;
public string Message//定义了可以通过Message = "",来对message进行赋值。
//因为不在构造函数中,所以是可选的
{
get
{return message;}
set
{message = value;}
}
/*
* 这部分可以简写如下
* public string Message{get;set;}
*/
}
#endregion

[DeBugInfo(45, "Zara Ali", "12/8/2012",
Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012",
Message = "Unused variable")]//前面定义时的AllowMultiple=ture允许了多次使用在同一地方
class Rectangle
{
protected double length;
protected double width;//定义两个受保护的(封装)的成员变量
public Rectangle(double l,double w)//构造函数,对两个成员变量进行初始化,公开的
{
length = l;
width = w;
}

[DeBugInfo(55, "Zara Ali", "19/10/2012",
Message = "Return type mismatch")]
public double GetArea()
{
return length * width;
}

[DeBugInfo(56, "Zara Ali", "19/10/2012")]//因为message是可选项,所以可以不给出
//不给出即为null,为空白
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(4.5, 7.5);//实例化
r.Display();//执行打印长、宽、面积

Type type = typeof(Rectangle);//让type对应这个Rectangle类
// 遍历 Rectangle 类的特性
foreach (Object attributes in type.GetCustomAttributes(false))//遍历Rectangle的所有特性
{
DeBugInfo dbi = (DeBugInfo)attributes;//强制转换
if(null != dbi)//dbi非空
{
Console.WriteLine("Bug on: {0}", dbi.BugNo);
Console.WriteLine("Developer: {0}", dbi.Developer);
Console.WriteLine("Last REviewed: {0}", dbi.LastReview);
Console.WriteLine("Remarks: {0}", dbi.Message);
}
}
// 遍历方法特性
foreach (MethodInfo m in type.GetMethods())//遍历Rectangle类下的所有方法
{
foreach (Attribute a in m.GetCustomAttributes(true))//遍历每个方法的特性,参数为true时表示继承子类的特性
{
DeBugInfo dbi = a as DeBugInfo;//通过 object 声明对象,是用了装箱和取消装箱的概念.
//也就是说 object 可以看成是所有类型的父类。
//因此 object 声明的对象可以转换成任意类型的值。
//通过拆装箱代替强制转换
if (null !=dbi)//同理打印
{
Console.WriteLine("BugFixApplication no: {0},for Method: {1}", dbi.BugNo, m.Name);
Console.WriteLine("Developer:{0}", dbi.Developer);
Console.WriteLine("Last Reviewed: {0}", dbi.LastReview);
Console.WriteLine("Remarks: {0}", dbi.Message);
}
}
}
Console.ReadKey();
}
}
}

属性

属性(Property)是域(Field)的扩展,且可使用相同的语法来访问。它们使用 访问器(accessors) 让私有域的值可被读写或操作。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public int a{get;}              //外界內界都是只读
int a{get;set;} //內界可读可写
public int a{get;private set;} //外界可读,內界可写

//属性可以被继承
class Program
{
public abstract class Person
{
public abstract string Name { get; set; }
public abstract int Age { get; set; }
}
public class Student : Person
{
public string Code { get; set; } = "N.A";
public override string Name { get; set; } = "N.A";
public override int Age { get; set; } = 0;
}
}

委托和事件

委托

C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。

声明和实例化委托:

1
2
public delegate int MyDelegate(string s);
MyDelegate del1 = new MyDelegate(str);

委托的多播

委托对象可使用 “+” 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。**”-“ 运算符可用于从合并的委托中移除组件委托**。使用委托的这个有用的特点,您可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的 多播(multicasting),也叫组播。

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
using System;

delegate int NumberChanger(int n);
namespace DelegateAppl
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}

public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}

static void Main(string[] args)
{
// 创建委托实例
NumberChanger nc;
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
nc = nc1;
nc += nc2;
// 调用多播
nc(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}

委托可以作为函数的参数被函数内部调用

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
using System;
using System.IO;

namespace DelegateAppl
{
class PrintString
{
static FileStream fs;
static StreamWriter sw;
// 委托声明
public delegate void printString(string s);

// 该方法打印到控制台
public static void WriteToScreen(string str)
{
Console.WriteLine("The String is: {0}", str);
}
// 该方法打印到文件
public static void WriteToFile(string s)
{
fs = new FileStream("c:\\message.txt", FileMode.Append, FileAccess.Write);
sw = new StreamWriter(fs);
sw.WriteLine(s);
sw.Flush();
sw.Close();
fs.Close();
}
// 该方法把委托作为参数,并使用它调用方法
public static void sendString(printString ps)
{
ps("Hello World");
}
static void Main(string[] args)
{
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);
sendString(ps1);
sendString(ps2);
Console.ReadKey();
}
}
}

可以使用匿名方法创建委托实例:

1
2
3
4
NumberChanger nc = delegate(int x)
{
Console.WriteLine("Anonymous Method: {0}", x);
};

事件

事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。C# 中使用事件机制实现线程间的通信。

通过事件使用委托

事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。

发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。

订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。

声明事件

1
2
public delegate void BoilerLogHandler(string status);  //先声明委托
public event BoilerLogHandler BoilerEventLog; //基于委托定义事件

实例:

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
using System;
namespace SimpleEvent
{
using System;
/***********发布器类***********/
public class EventTest
{
private int value;

public delegate void NumManipulationHandler();


public event NumManipulationHandler ChangeNum;
protected virtual void OnNumChanged()
{
if ( ChangeNum != null )
{
ChangeNum(); /* 事件被触发 */
}else {
Console.WriteLine( "event not fire" );
Console.ReadKey(); /* 回车继续 */
}
}


public EventTest()
{
int n = 5;
SetValue( n );
}


public void SetValue( int n )
{
if ( value != n )
{
value = n;
OnNumChanged();
}
}
}


/***********订阅器类***********/

public class subscribEvent
{
public void printf()
{
Console.WriteLine( "event fire" );
Console.ReadKey(); /* 回车继续 */
}
}

/***********触发***********/
public class MainClass
{
public static void Main()
{
EventTest e = new EventTest(); /* 实例化对象,第一次没有触发事件 */
subscribEvent v = new subscribEvent(); /* 实例化对象 */
e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 注册 */
e.SetValue( 7 );
e.SetValue( 11 );
}
}
}

集合

集合(Collection)类是专门用于数据存储和检索的类。这些类提供了对栈(stack)、队列(queue)、列表(list)和哈希表(hash table)的支持。大多数集合类实现了相同的接口。

集合(Collection)类服务于不同的目的,如为元素动态分配内存,基于索引访问列表项等等。这些类创建 Object 类的对象的集合。在 C# 中,Object 类是所有数据类型的基类。

常用集合:

  • ArrayList:可被单独索引的有序集合
  • Hashtable:可以使用键来访问集合中的元素
  • SortedList:可以使用键和索引来访问列表中的项
  • Stack:堆栈
  • Queue: 队列
  • BitArray: 点阵列,使用值1和0来表示二进制数组,当您需要存储位,但是事先不知道位数时,则使用点阵列。您可以使用整型索引从点阵列集合中访问各项,索引从零开始

泛型

泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。

您可以通过数据类型的替代参数编写类或方法的规范。当编译器遇到类的构造函数或方法的函数调用时,它会生成代码来处理指定的数据类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyGenericArray<T>
{
private T[] array;
public MyGenericArray(int size)
{
array = new T[size + 1];
}
public T getItem(int index)
{
return array[index];
}
public void setItem(int index, T value)
{
array[index] = value;
}
}

泛型特性

  • 它有助于您最大限度地重用代码、保护类型的安全以及提高性能。
  • 您可以创建泛型集合类。.NET 框架类库在 System.Collections.Generic 命名空间中包含了一些新的泛型集合类。您可以使用这些泛型集合类来替代 System.Collections 中的集合类。
  • 您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托
  • 您可以对泛型类进行约束以访问特定数据类型的方法。
  • 关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取

常用泛型

  • Dictionary: 键和值的集合
  • HashSet: 值的集合
  • LinkedList: 双重链接列表
  • List:可通过索引访问的对象的强类型列表
  • Queue:队列
  • SortedDictionary:根据键进行排序的键/值对的集合
  • SortedList:同上
  • SortedSet:表示按排序顺序维护的对象的集合
  • Stack:栈

快速上手CS
http://example.com/2023/01/10/快速上手CS/
作者
Chen Shuwen
发布于
2023年1月10日
许可协议