c#笔记-委托

委托

委托是方法的类型。
有了类型就可以声明方法的变量,参数,字段。然后再调用他。

很多新人很困惑,为什么要把方法做成变量,不直接去调用它呢?
这是因为在目前你的认知里,代码都是你一个人写出来的。
如果你的组长把任务分给了4位同学,你还能时刻监察其他人的代码吗?
如果你懒得自己写代码,找了一份扩展包呢?
还有c#自带的Console.WriteLine,这些代码也不是你写的。

他们写这些代码的时候,还不知道你写了什么方法,那要怎么在他们的代码里调用你的方法呢?
变量和参数,最开始就是用来解决编写时无法确定的东西。
委托是方法的类型,有了委托就能定义方法的变量。

Action

Action委托是返回void的委托。
他的各泛型版本对应了方法的参数类型。

Action<int> cw = Console.WriteLine;//用方法给委托赋值时不要带上方法调用的括号
cw(22);//调用委托就像调用那个方法一样

Func

Func委托是有返回值的委托。它只有泛型的版本。
它的最后一个泛型代表方法的返回值类型。前面的泛型是方法的参数类型。

Func<string, bool> join = string.IsNullOrEmpty;
bool empty = join(null);

自定义委托

预置的Action和Func委托类型以及他们的各泛型版本,在大多数情况下都能满足我们使用。
但一些情况下我们仍然必须创建自己的委托类型,例如参数中具有ref,out,in的时候。

委托是类型,是和类,结构,接口同级别的东西,所以委托可以直接定义在命名空间下。
委托类型使用delegate关键字声明,后面跟随一个方法签名,不能带有实现。

delegate bool TryParse<T>(string s, out T t);
TryParse<int> tryParse = int.TryParse;
bool parse = tryParse("123", out int t);

多播委托

在给委托类型的变量赋值时,方法不能带有括号。
带括号和参数的是调用方法,使用的值就是方法的返回值。
除非你的方法会返回一个委托。用来给委托进行赋值的,称为方法组

就像所有数组有共同基类Array,所有枚举有共同基类Enum,所有结构有共同基类ValueType
所有的委托类型的共同基类是Delegate。所以ActionFunc以及他们所有泛型版本的类型都叫委托类型。

在这里插入图片描述
委托也可以和方法组进行+-操作,但方法组和方法组不能。
当一个委托用+储存了多个方法时,这种情况称为多播委托

多播委托会记录下所有+过的方法组。在调用时会按照+时候的顺序依次调用。
但有返回值的委托,只有最后一个绑定方法组的返回值会获得。

可以一个多播委托,可以适用-来解绑方法组。
多播委托会在它记录的列表里查找需要解绑的方法组。
在找到匹配的方法组的时候,会解绑遇到的第一个匹配项。
如果找不到,那就无事发生,不会报错。

委托是引用类型

委托是引用类型,可以使用null作为值。
并且,在+-的过程中,遇到了null或减完以后自己变成了null也不会报错。
但对一个null委托进行调用还是会报错的。

Action<int>? cw = Console.WriteLine;
cw -= Console.WriteLine;
cw += null;
Console.WriteLine(cw == null);

/*if (cw != null)
{
	cw(20);
}*/
cw?.Invoke(20);//使用空传播代替if来终止委托调用。
			   //委托是一种类型,他有方法。Invoke就是调用这个委托。

匿名方法

在使用委托时,如果只是临时的使用一个方法,不想完整的写一遍方法的定义,可以使用匿名方法。
匿名方法只能在声明变量的时候使用,所以不能代替你直属类的方法的定义。

一个完整的匿名方法声明如下

Action<int>? _ =
	void (int i) =>
	{
    
    
		Console.WriteLine(i * i);
	};

和普通的方法声明相比,它没有名字(匿名),并且多了一个=>

一个匿名方法在以下条件时可以进行一些省略

  • 如果用作给参数或变量赋值,且变量用了泛型能推测出匿名方法中参数和返回值的类型,
    那么参数类型和返回值类型可以不写。
  • 在参数和返回值类型省略时,如果同时参数正好是1个,那么可以省略参数的括号。
  • 如果方法体内只有一条语句,那么可以省略方法体的大括号,和单语句的;

如果一个方法需要Action<int>类型的委托,使用匿名方法可以这样传入参数:

Hello(i => Console.WriteLine(i * i));

void Hello(Action<int> action)
{
    
    
	action?.Invoke(20);
}

多个参数但省略类型就像这样

Hello2((a, b) => $"{
      
      a * b}+{
      
      a + b}");

void Hello2(Func<int, int, string> func)
{
    
    
	Console.WriteLine(func(20, 30));
}

捕获变量

使用匿名方法或局部方法时,可以直接使用局部变量。
这种情况下,这个方法会显示捕获了变量。
在这里插入图片描述
这种情况下,他们获得的不是原始变量的复制,而是他们的引用。
也就是说,在方法内对捕获变量进行修改,也能作用到原来的变量上。

int a2 = 10;
int b2 = 20;

Action hello2 = () =>
{
    
    
	a2 = b2;
};

Console.WriteLine(a2);//10
hello2();
Console.WriteLine(a2);//20
b2 = 60;
hello2();
Console.WriteLine(a2);//60

不仅修改值会作用到原来的变量上,获取值也会得到实时的值而不是创建时的值。

Action? For = null;
for (int i = 0; i < 10; i++)
{
    
    
	For += () => Console.WriteLine(i);
}
For?.Invoke();//会得到10个10,而不是从0到9

捕获变量甚至可以让局部变量脱离它原本的作用域。
例如这里的临时迭代变量i,本来只应该在for循环内部存在。
因为捕获变量,把他带出来了。

事件

事件类似于属性,是一种带有访问器的成员。
事件的两个访问器是addremove,分别在对事件使用+=-=时触发。

当使用委托类型作为字段,使用属性的getset限制访问几乎没有意义。
有了get自己的委托就可能被他人随意调用。
有了set就会被他人影响到自己调用委托。

事件在委托类型前加event进行声明,然后像属性一样给他写两个访问器。
这两个访问器都是有一个类型和事件类型一样的参数,无返回值的方法。

class MyClass
{
    
    
	public event Action? Action
	{
    
    
		add
		{
    
    
			action += value;
			value?.Invoke();
		}
		remove
		{
    
    
			value?.Invoke();
			action -= value;
		}
	}
	private Action? action;
}

通常情况下事件仅允许出现在+=-=的左边。
这样就阻止了别人调用这个委托,或者直接设置为null

委托是引用类型,如果要进行解绑必须要拿到原本的那个实例。

  • 成员方法和局部方法都是静态的,他们都只有一份实例。
  • 使用成员方法进行解绑,意味着你需要具有这个方法的访问权限。
  • 局部方法只能在定义方法的内容使用,所以只有使用者能进行解绑。
  • 但是匿名方法是动态创建的,每一次声明一个匿名方法都是不同的实例。
    所以除非使用变量储存了这个匿名方法,否则没人能对他进行解绑。

自动实现的事件

像属性一样,如果你省略掉访问器的具体逻辑,
那么编译器会自动帮你添加一个匿名字段,
并为这个事件添加控制这个匿名字段的逻辑。

但事件的访问器不是可选的,这两个访问器都必须存在。
所以,省略访问器逻辑是连声明访问器的括号都不用写。

class MyClass2
{
    
    
	public event Action? Action;
	public void Invoke()
	{
    
    
		Action?.Invoke();
	}
}

属性的访问和赋值以及其他使用,跟直接使用变量是一样的。
所以自动实现的属性和普通字段使用起来感觉没有什么差别。

但是事件只能出现在+=-=前面。而委托可以像完整的变量一样进行访问。
所以,如果自动实现的事件仍然只能使用+=-=,那就无法调用到这个匿名委托字段了。
满意以下条件时,可以把事件当作委托直接进行访问。

  • 必须是有匿名委托字段的。这要求它必须是一个自动实现的事件。
  • 必须是能存在字段的,这要求它不是接口的实例事件。
  • 必须是能定义访问器逻辑的,这要求它不是抽象的事件。
  • 只有定义类才能访问,即便派生类或外部具有这个事件访问权限。

猜你喜欢

转载自blog.csdn.net/zms9110750/article/details/130649414