委托
delegate:函数指针
多播委托:可以引用一个或者一组方法。如果返回值不是null,则只会保留最后一个方法的返回值,其余的被丢弃。
Func:有返回值
Action:没有返回值
相同签名的委托是互不相同的
委托也有协变和逆变
事件
标准事件模式 EventArgs,可以看委托事件
事件也可以是virtual,override,abstract,sealed
Lambda
1.Lambda表达式是一种可以替代委托实例的匿名方法。实际上编译器将重新建立一个私有方法,添加进去。
2.(参数) => 表达式
3.Lambda可以引用方法内定义的局部变量和方法的参数。Lambda表达式所引用的外部变量称为捕获变量。捕获变量的表达式称为闭包
4.捕获的变量会在真正使用时赋值,而不是在捕获时赋值
匿名方法
和lambda表达式差不多
try catch finally
异常筛选器,某个异常的特定方式被触发后才会捕获异常
using
使用using语句必须实现IDispose接口,using语句可以使对象一定会被销毁
使用using完全等价于
using(申请资源)
{
}
===
{
try{申请资源}
finally{销毁资源}
}
异常
抛出异常 throw new Exception
可以将上面的式子看成是一个表达式
重新抛出异常:throw,在某些情况下可以抛出一个更具体或者更宽泛的异常,而且可以将原来的异常信息当做参数传给新异常。
TryXXX模式
对于参数不确定计算的时候,可以有2中方法,一种是不符合就抛出异常,另一种是TryXXX模式,不符合返回false,同时定义一个默认值
Yield
yield return 可以返回多次,如果语句带有try,那么只有finally才可以使用,其他情况都不能出现,即出现catch就不能用
yield break 可以提前结束
可空类型
1.引用类型可以使用空引用表示一个不存在的值,但是值类型不行。
2.如果想要使值类型可以表示为空,则int?i = null
3.int?实际上翻译成了Nullable<int>类型,里面重要的就是Value和HasValue
4.int?的默认值为null
5.在对int?进行运算时,加减或者比较时,会先判断HasValue,然后取Value,在计算
6.如果int? a = null,则a与int数做计算为null
扩展方法
1.扩展方法允许在现有类型上扩展新的方法而无需修改原始类型的定义。必须是静态类的静态方法,而第一个参数需要用this修饰符。
2.扩展方法可以定义成扩展方法链
3.任何兼容的实例方法都会优先于扩展方法,如果出现相同的签名,则只能普通的调用 AAA.BBB(xxx);
4.如果有2个签名相同的扩展方法,则类型更具体的先调用,否则只能用传统的方法
匿名类型
var a = new { name = "zhang", age = 1 };
1.相当于在内部新建了一个内部类,里面有name和age属性,a则是该类的实例。
2.将函数返回类型指定为var是非法的
3.也可以生成匿名类型的数组
元组
var b = ("bib", "23");
Console.WriteLine(b.Item1);
1.元组可以做到所有匿名类型可以做到的事情
2.元组是值类型,是可读可写的
3.方法返回元组
static (string, int) Get => ("aaa", 23);
4.元组可以作为泛型的参数
5.创建元组时,可以为元素起有意义的名字来替代item1,item2。当然是用item1也不会出错
var b = (name:"bib", ahe:"23");
(string name,int age) b = ("bib", 23);
6.如果元组(按顺序)对应的元素类型相同,则元组时类型兼容的。也就是命名并不会影响什么。
7.元组就是ValueTuple
8.也可以使用ValueTuple来创建元组
9.元组的解构:将元组拆分开来,和声明一个元组类似
//元组的声明
(string name,int age) b = ("bib", 23);
//元组的解构
(string name, int age) = ("bic", 43);
10.元组实现了IComparable接口,所以可以进行比较
11.Tuple类基本不用了
特性
1.特性就是写在属性或类上[Attribute]
2.特性一般以Attribute结尾
3.特性可以有参数,参数分为位置参数和命名参数。位置参数对应于特性类型的公有构造函数的参数,命名参数对应于特性的公有字段或属性
4.特性的目标一般为类或者类的成员,也可以修饰程序集,但是需要额外指定
5.同一个目标可以指定多个特性,特性可以写在一个[]之内,也可以分开
调用者信息特性
方法可以获取调用该方法的方法的信息
static void Foo(
//获取方法名称
[CallerMemberName]string name = null,
//获取文件路径
[CallerFilePath]string path=null,
//获取调用行数
[CallerLineNumber]int line=0)
{
Console.WriteLine(name);
Console.WriteLine(path);
Console.WriteLine(line);
}
动态绑定
动态绑定
Duck duck = new Duck();
duck.Quack();
//Error
//object duck2 = new Duck();
//duck2.Quack();
//Success
dynamic duck3 = new Duck();
duck3.Quack();
自定义绑定
class Duck:DynamicObject
{
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
Console.WriteLine(binder.Name + " method was called");
result = null;
return true;
}
}
static void Main(string[] args)
{
dynamic duck = new Duck();
duck.Quack();
duck.Quacc();
Console.ReadLine();
}
语言绑定
static void Main(string[] args)
{
int x = 1;
int y = 2;
Console.WriteLine(Mean(x, y));
Console.ReadLine();
}
static dynamic Mean(dynamic x, dynamic y) => (x + y) / 2;
会降低安全性
dynamic和object类有很强的关系,甚至可以说是相等的。
dynamic类型可以转换,但是只有在类本身可以转换的时候才能转换
int可以转成long,那么dynamic类型的int可以转为long。
int不能转为short,那么dynamic类型的int不可以转为short
动态表达式:
对于返回值为void的方法如果用dynamic去接受,则会在运行时报错
有dynamic变量参与的运算,其结果也是dynamic的
无动态接收者的动态调用:
dynamic类型可以作为参数传递,函数签名只会检查参数个数,而不会检查具体的参数类型
运算符重载
1.通过operator关键字
2.必须是static和public
class Duck
{
public Duck(int num)
{
Num = num;
}
public int Num { get; }
public static Duck operator + (Duck x, int num)
{
return new Duck(x.Num + num);
}
}
3.重载显示和隐式转换
public static implicit operator int (Duck x) => x.Num;
public static explicit operator Duck(int a) => new Duck(a);
static void Main(string[] args)
{
Duck duck = new Duck(10);
duck += 20;
int num = duck;
Duck duck1 = (Duck)23;
Console.WriteLine(num);
Console.ReadLine();
}
as和is会自动忽略自定义转换
4.重载true和false
不安全的代码和指针
unsafe 指针 fixed保护块不被移动
预处理指令
预处理指令必须放到文件的最上面
#warning text提示警告
#error text 提示错误信息
#pragma warning [disable|restore]取消警告
Conditional只对类和方法有效
XML文档
<summary>:对提示需要显示的信息
<remarks>:类型或成员的附加描述信息,文档生成器会获得这些信息
<return>:对返回值得解释
<exception>:抛出异常的解释
。。。
可以使用Sandcastle来自动生成文档