0%

关于C#的笔记

这份笔记……在我全部整理完想快速发布的时候,编辑器崩坏,记录全丢惹(atom你真的不行了).
……不准备再记一遍惹反正看过有点印象在脑子里也足够.
只剩下一点碎片的截图和没搞懂的概念重新看一遍.





C# 索引器 - Indexer

索引器(Indexer) 允许一个对象可以像数组一样被索引。
当您为类定义一个索引器时,该类的行为就会像一个 虚拟数组(virtual array) 一样。

索引器(Indexer)的用途

索引器的行为的声明在某种程度上类似于属性(property)。就像属性(property),您可使用 get 和 set 访问器来定义索引器。但是,属性返回或设置一个特定的数据成员,而索引器返回或设置对象实例的一个特定值。换句话说,它把实例数据分为更小的部分,并索引每个部分,获取或设置每个部分。

定义一个属性(property)包括提供属性名称。索引器定义的时候不带有名称,但带有 this 关键字,它指向对象实例。

重载索引器(Indexer)

索引器(Indexer)可被重载。索引器声明的时候也可带有多个参数,且每个参数可以是不同的类型。没有必要让索引器必须是整型的。C# 允许索引器可以是其他类型,例如,字符串类型。



委托 - Delegate

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

有点像unity的unity event?

委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。

声明委托类型.

1
delegate void MyDel( int x );

变量声明.

1
MyDel delVar;

两种创立委托的方式.

1
2
3
delVar = new MyDel(myInstObj.MyM1);

delVar = myInstObj.MyM1; //快捷方式,因为declare过了

委托可以使用格外的运算符来组合.

1
2
3
4
MyDel delA = myInstObj.MyM1;
MyDel delB = SClass.OtherM2;

MyDel delC = delA + delB;//C为新生成,不会更改A或B
1
2
3
MyDel delVar = inst.MyM1;
delVar += SC1.m3;
delVar += X.Act;

使用+=运算符的时候,创造了新的委托,然后把这个新的委托赋值给delVar.

可以用-=移除委托.

如果在调用列表中的方法中有多个实例, -=运算符将从列表最后开始搜索,并移除第一个与方法匹配的实例.
试图删除委托中不存在的方法将无效.
试图调用空委托会抛出异常.可以跟null比较.

调用委托的两种方式.

1
2
3
if (delVar != null)
{ delVar(55);} // 调用
delVar?.Invoke(65); // 使用Invoke和空条件运算符

调用委托时,它使用相同的参数来执行调用列表中的每一个方法.

如果委托有返回值并且在调用列表中有一个以上的方式时.

  1. 调用列表中最后一个方法返回的值就是委托调用返回的值.
  2. 调用列表中所有其他方法的返回值都会被忽略.

如果委托有引用参数,参数值会根据调用列表中的一个或多个方法的返回值而改变.
在调用委托列表中的下一个方法时,参数的新值(不是初始值)会传给下一个方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
delegate void MyDel( ref int x );

class MyClass {
public void Add2(ref int x) { x += 2; }
public void Add2(ref int x) { x += 3; }

static void Main() {
MyClass mc = new MyClass();
MyDel mDel = mc.Add2;
mDel += mc.Add3;
mDel += mc.Add2;

int x = 5;
mDel(ref x);
}
}
// this will go through Add2(x = 5); Add3(x = 7); Add2(x = 10);


匿名方法(anonymous method)是在实例化委托时内联(inline)声明的方法.
可以无需使用独立的具名方法.

1
2
3
4
5
6
7
8
delegate int OtherDel(int InParam);
static void Main(){
OtherDel del = delegate(int x){
return x + 20;
}
Console.WriteLine("{0}", del(5));
Console.WriteLine("{0}", del(6));
}

如果委托声明的参数列表包含了params参数,那么匿名方法的参数列表将忽略params关键字.
Params: It is used as a parameter which can take the variable number of arguments of specific data type.



事件 - Event

事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些出现,如系统生成的通知。
事件像是包含了一个私有的额委托.

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

订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。
由订阅者提供的方法称为回调方法,还可以将它们称为事件处理程序.

事件处理程序(event handler) 由订阅者注册到事件的方法,由发布者触发事件时执行。事件处理程序方法可以定义在事件所在的类或结构中,也可以定义在不同的类或结构中。
触发(raise)事件 调用(invoke)或触发(fire)事件的术语。

事件提供了对它控制委托的结构化访问,就是无法直接访问委托.
事件只能添加,删除或调用事件处理程序.
事件被触发时,它调用委托来依次调用调用列表中的方法.



泛型 - Generic

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

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

由尖括号和T构成的字符串表明T是类型的占位符(不一定是字母T,它可以是任何标识符)。在类声明的主体中,每一个T都会被编译器替换为实际类型。

使用步骤:声明类和创建类的实例。

声明.

1
2
3
4
5
class SomeClass < T1, T2 >
{
public T1 SomeVar;
public T2 OtherVar;
}

创建.

1
class SomeClass < short, int >

替代类型参数的真实类型叫做类型实参(type argument).这里是short和int.
可以给argument增加约束(constraint).
where TypeParam: constraint, constraint, …

1
2
3
4
5
6
7
class MyClass < T1, T2, T3>
where T1: IEnumerable,
where T2: Customer,
where T3: IComparable
{
...
}

赋值兼容性不适用,因为两个委托没有继承关系。
仅将派生类型用作输出值与构造委托有效性之间的常数关系叫协变(convariance)。为了让编译器知道这是我们的期望,必须使用out关键词标记委托声明中的类型参数。
协变关系允许程度更高的派生类型处于返回及输出位置。

逆变(contravariance)则相反.



枚举器 & Foreach

Array中自带一个叫做枚举器(enumerator)的对象.
包括3个函数成员:Current, MoveNext以及Reset.

Current返回序列中当前位置项的属性.
是只读属性.
返回Object类型的引用,所以可以返回任何类型的对象.

MoveNext是把枚举器位置前进到集合中下一项的方法.也返回Boolean,指示新的位置是有效位置还是已经超过了序列的尾部.
True新的位置是有效的,false反之.
Enumerator的原始位置在序列中的第一项之前,因此MoveNext必须在第一次使用Current之前调用.

可枚举类是指实现了IEnumerable接口的类.IEnumerable接口只有一个成员,GetEnumerator方法,它返回对象的枚举器.



特性 - Attribute

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

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



反射 - Reflection

Using System.Reflection

有关程序及其类型的数据被称为元数据,它们保存在程序的程序集中.
程序在运行时,可以查看其他程序集或其本身的元数据.运行中的程序查看本身的元数据或其他程序的元数据的行为叫做反射.
您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。



C#中的类型,包括预定义类型(int, long和string等)、BCL中的类型(console, IEnumerable等)以及用户定义类型(MyClass, Mydel等).
BCL中声明了Type的抽象类,被设计用来包含类型的特征.
对于程序中用到的每一个类型,CLR都会创建一个包含这个类型信息的Type类型的对象.
不管创建的类型有多少个实例,只有一个Type对象会关联到所有这些实例.



优点:

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

缺点:

性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。

使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。



反射(Reflection)有下列用途:

它允许在运行时查看特性(attribute)信息。
它允许审查集合中的各种类型,以及实例化这些类型。
它允许延迟绑定的方法和属性(property)。
它允许在运行时创建新类型,然后使用这些类型执行一些任务。



特性(attribute)是一种允许我们向程序的程序集添加元数据的语言结构.
它是用于保存程序结构信息的特殊类型的类.
将应用了特性的程序结构(program construct)叫做目标(target).
设计用来获取和使用元数据的程序(比如对象浏览器)叫作特性的消费者(consumer).

1
2
3
4
5
[ MyAttribute("Simple class", "Version 3.57")]
public class MyOtherClass
{
...
}


多线程 - Async/Await

线程 被定义为程序的执行路径。
每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。

线程是轻量级进程。
一个使用线程的常见实例是现代操作系统中并行编程的实现。使用线程节省了 CPU 周期的浪费,同时提高了应用程序的效率。

到目前为止我们编写的程序是一个单线程作为应用程序的运行实例的单一的过程运行的。但是,这样子应用程序同时只能执行一个任务。为了同时执行多个任务,它可以被划分为更小的线程。

异步的方法在完成其所有工作之前就返回到调用方法.Async/await由3个部分组成.

调用方法(calling method):调用异步方法,在异步方法执行其任务的时候继续执行.可能在相同线程上,可能在不同线程上.
异步(async)方法:该方法异步执行其工作,然后立即返回到调用方法.
await表达式:用于异步方法内部,指明需要异步执行的任务.可以包含多个await表达式,一个都没会报错.



线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。

下面列出了线程生命周期中的各种状态:

未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。
就绪状态:当线程准备好运行并等待 CPU 周期时的状况。
不可运行状态:下面的几种情况下线程是不可运行的:
• 已经调用 Sleep 方法
• 已经调用 Wait 方法
• 通过 I/O 操作阻塞
死亡状态:当线程已完成执行或已中止时的状况。



主线程
在 C# 中,System.Threading.Thread 类用于线程的工作。它允许创建并访问多线程应用程序中的单个线程。进程中第一个被执行的线程称为主线程。
当 C# 程序开始执行时,主线程自动创建。使用 Thread 类创建的线程被主线程的子线程调用。您可以使用 Thread 类的 CurrentThread 属性访问线程。



回调模式.
一旦初始线程发起了异步方法,它会自己管自己,不再考虑同步.
当异步方法调用结束之后,系统调用一个用户自定义的方法来处理结果,并且调用委托的endInvoke方法.
这个用户自定义的方法叫做回调方法回调.



命名空间 - Namespace

命名空间是共享同一命名空间名的一组类型定义.
用于避免不同代码库(ddl)内的function重复名.
可以把命名空间名视为一个字符串(在字符串中可以使用点),它加在类名或类型名的前面并且通过点进行分隔.
包括命名空间名,分隔点,以及类名的完整字符串叫做类的完全限定名.
命名空间是共享命名空间名的一组类和类型.

命名指南:

以公司名称开头.
在公司名之后跟着技术名.
不要与类或类型名相同.

Namespace不是封闭的,可以在该源文件中再次声明它,以对它增加更多的类型声明.



预处理指令 - Preprocessor Directive

指示编译器如何处理源代码.



String



可空类型



GUI

graphical user interface (GUI)

 Don’t perform any time-consuming action on the UI thread.
 Don’t access any UI controls other than on the UI thread.