无参属性
建议把所有的字段都设置为false,所有想公开的都设置为public属性,以达到能够封装的目的。
我已经懂了所以基础的就不写了,具体可以自行搜索。(属性都不会你写什么C#)
自动实现的属性
只写了set; get;
而没有定义具体set什么get什么的属性,叫做自动实现的属性,C#会自动声明一个私有字段和get、set方法。
比如
1 | public class Program |
用ILDasm可以看到其实是生成了两个方法get_ID
, set_ID
,以及一个字段<ID>k__BackingField
。
看一下get_ID
的IL代码:
1 | .method public hidebysig newslot specialname virtual final |
可以看到这个方法也是specialname
的,所以就算你在自己类里面多定义了一个同名方法,也不会被意外调用。
注意:使用AIP(auto implement property)的时候必须同时写set和get。
使用属性的注意事项
属性说到底是个方法,如果消耗很大,千万不要频繁使用。因为说到底是个方法,看起来是个字段,所以最好把它当做一个方法来看,不要乱用。
匿名类型
利用C#对的匿名类型功能,可以用很简洁的语法来自动生命不可变的元组类型。
1 | var ol = new { Name = "abo", Age = 99 }; |
查看ILDasm,可以看到生成了一个匿名类,其中包含了属性Age
和Name
,属性是。这其中还重写了Equals
,GetHashCode
,ToString
方法。
其中Equals
方法会比较每个字段是否相同。
由于重写了GetHashCode所以可以被放在哈希表里。
如果定义了两个字段一样的匿名实例,最后其实用的是同一个匿名类。
匿名类经常被用在LINQ中,返回一个匿名类型实例,我们用var去承接就可以了。
有参数性
其实就是给自己加一个索引器。
1 | class Program |
调用属性访问器方法时的性能
对于简单的get和set访问器方法,JIT编译器会将代码内联,避免了性能上的损失。
属性访问器的可访问性
可以为set或者get设置不同的访问性,但是只能同时在内部定义其中一个。如果想定义两个不同的访问性,应该这样:
1 | public bool Property |
下面讲11章 事件。
常见事件用法
先贴一下所有IL命令的reference文档:OpCodes Class
这本书对事件写得异常复杂,我这里就写一下自己的理解吧。
1 | class Program |
先贴一下反编译的图。
可以看到当我们定义了一个handler的时候,编译器会帮我们生成一个同名的封闭类,它继承于System.MulticastDelegate
,其中有两个方法需要重视:
一个是构造方法.ctor
,参数有两个,一个是Object类型,一个是int类型。
另一个是Invoke
方法,从字面上了解,应该是可以被调用的意思。
接下来再看我们在Program的构造方法中那几句话翻译成IL是怎么样的:
1 | .method public hidebysig specialname rtspecialname |
首先,Program还是要先调用一下基类Object的构造方法(前面几篇讲过)。
接下来的几句话对应着OnMsgReceived
的自加操作:
- ldsfld(load static field) 让
OnMsgReceived
入栈 - ldarg.0 作为第0个参数
- ldftn(load function) 让
OnMsgReceivedMain
方法入栈 - newobj new一个新的实例,调用
OnMsgReceivedHandler
类的构造方法 - 调用
System.Delegate::Combine(Delegate a, Delegate b)
方法 - castclass 类型转换,转成OnMsgReceivedHandler类型
- stsfld 替换静态字段的值
首先,我们定义了一个event的时候,实际上是定义了一个OnMsgReceivedHandler
类型的实例,这个实例在这里名为OnMsgReceived
总结一下就是当进行自加操作的时候:
- 新建一个
OnMsgReceivedHandler
对象 - 用
System.Delegate::Combine(Delegate a, Delegate b)
方法把静态的OnMsgReceived
和新生成的实例合并在一起, - 返回得到的实例被转化成OnMsgReceivedHandler类型
- 用
stsfld
操作把新得到的实例替换到静态引用上去
写成C#伪代码大概是:
1 | class Program |
知道了自加操作其实是生成一个新的Delegate后替换掉老的,那么后面的分发操作就好理解了。首先要判断这个delegate是否为null,MulticastDelegate重写了!=和==操作符,所以可以做出正确的判断。
当判断到这个delegate是有实际内容的之后,接下来就只是简单地执行它的Invoke()
方法,就可以了。
泛型
代码爆炸
在AppDomain中,每一个出现的泛型类都会被编译成一个完全不同的类。也就是说List<A>
和List<B>
会是完全不同的代码。如果使用了太多不一样的泛型,有可能会造成代码爆炸。
主要约束
用where
关键字,主要约束包括了class
和struct
。
次要约束
同样用where
关键字,次要约束包括了继承的基类和实现的接口。
构造器约束
where T : new()
表示该泛型应该实现了公共无参构造器。
接口
普通的方法没有什么好讲的。
哦,被实现的方法可以是virtual的。
实现多个具有同样方法签名的接口
必须使用显式接口方法实现。
1 | public interface IWindow |
由于实现了两个签名一样的方法,所以在调用的时候要转成对应的接口才能调用。
1 | Dirived d = new Dirived(); |
要用基类还是接口
接口是can do,基类是is a。