委托概述
什么是委托
- 委托是一种数据类型,像类一样(可以定义委托类型变量),委托类型变量是一个存储方法的变量;
public delagate 返回值类型 委托类型名(存储的方法的参数)
public delegate void MyDelegate();
namespace DelegateDemo1
{
internal class Program
{
static void Main(string[] args)
{
MyDelegate m1 = new MyDelegate(M1);
m1();
}
static void M1()
{
Console.WriteLine("M1是一个无参无返回值的方法!");
}
}
public delegate void MyDelegate();
public class MyClass
{
}
}
使用场景
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DelegateDemo1
{
public delegate void WriteTimeDelegate();
public class ProxyPerson
{
// 调用DoSomething时,需要一个委托类型的方法
// 由于定义委托时,定义的是一个无参无返回值类型的委托,因此,这个参数也就是一个无参无返回值的方法
public void DoSomething(WriteTimeDelegate writeTimeDelegate)
{
Console.WriteLine("==========================");
Console.WriteLine("=======复杂逻辑代码=========");
// 想实现的功能:当执行完第二句代码时,输出一下系统时间
if(writeTimeDelegate != null)
{
writeTimeDelegate();
}
Console.WriteLine("=======复杂逻辑代码=========");
Console.WriteLine("==========================");
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DelegateDemo1
{
public class DelegatePerson1
{
public void WriteTimeToDB()
{
Console.WriteLine($".sql,{System.DateTime.Now.ToString()}");
}
}
public class DelegatePerson2
{
public void WriteTimeToFile()
{
Console.WriteLine($".txt,{System.DateTime.Now.ToString()}");
}
}
}
namespace DelegateDemo1
{
internal class Program
{
static void Main(string[] args)
{
ProxyPerson proxyPerson = new ProxyPerson();
DelegatePerson1 delegatePerson1 = new DelegatePerson1();
proxyPerson.DoSomething(delegatePerson1.WriteTimeToDB);
DelegatePerson2 delegatePerson2 = new DelegatePerson2();
proxyPerson.DoSomething(delegatePerson2.WriteTimeToFile);
}
}
}
意义
- 代码复用,把能复用的代码复用,把变化的部分封装起来,用传进来的委托变量(就是方法)代替;
- 有点像是Vue里的插槽,在可复用的代码中留了个插槽,将来在调用时,想在这个插槽中写什么都可以通过委托传进来。
- Winform的控件就是使用委托来实现的。
案例 窗体之间传值
namespace WinFormDelegate
{
public delegate void MyDelegate(string text);
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Form2 f2 = new Form2(textBox1.Text.Trim(), UpdateText);
f2.Show();
}
public void UpdateText(string val)
{
this.textBox1.Text = val;
}
}
public delegate void UpdateTextDelegate(string val);
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinFormDelegate
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
public Form2(string n, UpdateTextDelegate updateTextDelegate) :this()
{
this.textBox1.Text = n;
this._updateText = updateTextDelegate;
}
private UpdateTextDelegate _updateText;
private void button1_Click(object sender, EventArgs e)
{
_updateText(this.textBox1.Text);
this.Close();
}
}
}
- 这个例子中,Form1把操作它自己的方法传递传递给Form2,就好像在它自己身上留了个空,先去调用Form2,操作Form2,当Form2想要做的事件做完后,再调用Form1的方法,把主线拉回到Form1,在这个过程中就可以实现窗体之间的通信了。
匿名方法 与 Lambda表达式
- 由于委托变量需要方法作为参数,那么就有这么一种场景,这一个方法只需要使用一次,这种情况下,不必声明方法的名称,只需要创建一个匿名方法即可,而Lambda表达式就是对匿名方法的简化。
- 匿名方法不能直接在类中定义,而是在给委托变量赋值的时候,就现做一个方法作为委托变量的方法。
namespace DelegateDemo1
{
internal class Program
{
static void Main(string[] args)
{
MyDelegate myDelegate = delegate ()
{
Console.WriteLine("这是一个匿名方法");
};
myDelegate();
MyDelegate1 myDelegate1 = delegate (string msg)
{
Console.WriteLine($"这是一个有参无返回值的匿名方法,{msg}");
};
myDelegate1("你好");
MyDelegate2 myDelegate2 = delegate (int n1, int n2, int n3)
{
return n1 + n2 + n3;
};
int result = myDelegate2(1,2,3);
Console.WriteLine(result);
}
}
public delegate void MyDelegate();
public delegate void MyDelegate1(string msg);
public delegate int MyDelegate2(int n1,int n2,int n3);
}
- Lambda表达式由于委托限制了传参的类型,所以从来不需要声明类型。
namespace DelegateDemo1
{
internal class Program
{
static void Main(string[] args)
{
MyDelegate myDelegate = () =>
{
Console.WriteLine("这是一个Lambda表达式");
};
myDelegate();
MyDelegate1 myDelegate1 = msg =>
{
Console.WriteLine($"这是一个有参无返回值的Lambda表达式,{msg}");
};
myDelegate1("你好");
MyDelegate2 myDelegate2 = (n1, n2, n3) =>
{
return n1 + n2 + n3;
};
int result = myDelegate2(1,2,3);
Console.WriteLine(result);
}
}
public delegate void MyDelegate();
public delegate void MyDelegate1(string msg);
public delegate int MyDelegate2(int n1,int n2,int n3);
}
泛型委托
- C# 已经定义好了委托,不需要我们再去声明一个委托类型
- Action 用于声明无参无返回值类型的委托变量
- Action<> 用于声明有参(最多16个参数重载)无返回值类型的委托变量
- Func<> 只有泛型版本的,没有非泛型版本的,因为必须告诉Func返回值类型,就通过参数声明;
- Func<> 最后一个参数一定是返回值类型
namespace DelegateDemo1
{
internal class Program
{
static void Main(string[] args)
{
Action myDelegate = () =>
{
Console.WriteLine("这是一个Lambda表达式");
};
myDelegate();
Action<string> myDelegate1 = msg =>
{
Console.WriteLine($"这是一个有参无返回值的Lambda表达式,{msg}");
};
myDelegate1("你好");
Func<int,int,int,int> myDelegate2 = (n1, n2, n3) =>
{
return n1 + n2 + n3;
};
int result = myDelegate2(1,2,3);
Console.WriteLine(result);
}
}
}
多播委托(委托链,委托的组合)
namespace DelegateDemo1
{
internal class Program
{
static void Main(string[] args)
{
Action<string> myDelegate1 = M1;
myDelegate1 += M2;
myDelegate1 += M3;
myDelegate1 += M4;
myDelegate1 -= M3;
myDelegate1("你好");
}
static void M1(string msg)
{
Console.WriteLine($"M1 {msg}");
}
static void M2(string msg)
{
Console.WriteLine($"M2 {msg}");
}
static void M3(string msg)
{
Console.WriteLine($"M3 {msg}");
}
static void M4(string msg)
{
Console.WriteLine($"M4 {msg}");
}
}
}
- 用ILSpy打开;
- 发现IL是用combine连接在一起的,好像字符串一样,并没有创建新的;
- 而是每次创建一个委托对象,每次跟之前的委托对象一起放进一个委托数组中;
- 多播委托在调用时,一次遍历委托对象,最终实现把所有委托对象都调用完成;
- 委托跟字符串一样,具有不可变性;
private static void Main(string[] args)
{
Action<string> myDelegate1 = M1;
myDelegate1 = (Action<string>)Delegate.Combine(myDelegate1, new Action<string>(M2));
myDelegate1 = (Action<string>)Delegate.Combine(myDelegate1, new Action<string>(M3));
myDelegate1 = (Action<string>)Delegate.Combine(myDelegate1, new Action<string>(M4));
myDelegate1 = (Action<string>)Delegate.Remove(myDelegate1, new Action<string>(M3));
myDelegate1("你好");
}
- 其实委托就是一个类,并且是一个密封类sealed;
- 用ILSpy可看见,它继承MulticastDelegate:Delegate,并且有Invoke,BeginInvoke,EndInvoke三个方法
public abstract class MulticastDelegate : Delegate
public virtual extern IAsyncResult BeginInvoke(AsyncCallback callback, object @object);
public virtual extern void EndInvoke(IAsyncResult result);
public virtual extern void Invoke();
namespace DelegateDemo1
{
internal class Program
{
static void Main(string[] args)
{
MyDelegate md = new MyDelegate(M1);
md = (MyDelegate)Delegate.Combine(md, new MyDelegate(M2));
md = (MyDelegate)Delegate.Combine(md, new MyDelegate(M3));
md = (MyDelegate)Delegate.Combine(md, new MyDelegate(M4));
Delegate[] delegates = md.GetInvocationList();
for (int i = 0; i < delegates.Length; i++)
{
(delegates[i] as MyDelegate)("你好");
}
}
static void M1(string msg)
{
Console.WriteLine($"M1 {msg}");
}
static void M2(string msg)
{
Console.WriteLine($"M2 {msg}");
}
static void M3(string msg)
{
Console.WriteLine($"M3 {msg}");
}
static void M4(string msg)
{
Console.WriteLine($"M4 {msg}");
}
}
public delegate void MyDelegate(string msg);
}