开放的编程资料库

当前位置:我爱分享网 > C#教程 > 正文

C#委托

在本文中,我们使用C#中的委托。

委托定义

委托是一种类型安全的方法指针。它引用具有特定参数列表和返回类型的方法。

委托是一种引用类型。但是委托不是指一个对象,而是指一个方法。我们可以通过委托实例调用可以引用的方法。

委托用于以下情况:

  • 事件处理程序
  • 回调
  • 将方法作为方法参数传递
  • LINQ
  • 设计模式的实现

没有什么是常规方法不能用委托完成的。使用委托是因为它们带来了几个优点。它们促进了应用程序的灵活性和代码重用。当我们需要决定在运行时调用哪个方法时,我们使用委托。

C#使用委托

我们有一些简单的例子展示了如何使用委托。

var md = new MyDelegate(MyCallback);
md();

void MyCallback()
{
    Console.WriteLine("Calling callback");
}

delegate void MyDelegate();

我们声明一个委托,创建委托的实例并调用它。

var md = new MyDelegate(MyCallback);

我们创建委托实例。调用时,委托将调用Callback方法。

md();

我们调用委托。

delegate void MyDelegate();

这是我们的委托声明。它不返回任何值,也不接受任何参数。

$ dotnet run
Calling callback

我们可以使用不同的语法来创建和使用委托。

MyDelegate del = MyCallback;
del();

void MyCallback()
{
    Console.WriteLine("Calling callback");
}

delegate void MyDelegate();

我们可以在创建委托实例时节省一些输入。

MyDelegate del = MyCallback;

这是创建委托的另一种方式。我们直接指向方法名。

C#委托指向不同的方法

随着时间的推移,委托可以指向不同的方法。

var per = new Person("Fabius", "Maximus");

var nDelegate = new NameDelegate(per.ShowFirstName);
nDelegate("Call 1:");

nDelegate = new NameDelegate(per.ShowSecondName);
nDelegate("Call 2:");


public delegate void NameDelegate(string msg);

public class Person
{
    public string firstName;
    public string secondName;

    public Person(string firstName, string secondName)
    {
        this.firstName = firstName;
        this.secondName = secondName;
    }

    public void ShowFirstName(string msg)
    {
        Console.WriteLine($"{msg} {this.firstName}");
    }

    public void ShowSecondName(string msg)
    {
        Console.WriteLine($"{msg} {this.secondName}");
    }
}

在示例中,我们有一个代表。该委托用于指向Person类的两个方法。这些方法由委托调用。

var nDelegate = new NameDelegate(per.ShowFirstName);
nDelegate("Call 1:");

我们创建一个指向ShowFirstName方法的新委托实例。稍后我们通过委托调用该方法。

public delegate void NameDelegate(string msg);

委托是使用delegate关键字创建的。委托签名必须与委托调用的方法的签名相匹配。

$ dotnet run
Call 1: Fabius
Call 2: Maximus

两个名字都是通过委托打印的。

C#多播委托

多播委托是一种持有对多个方法的引用的委托。多播委托必须仅包含返回void的方法,否则会出现运行时异常。

var del = new MyDelegate(Oper.Add);

del += new MyDelegate(Oper.Sub);
del(6, 4);

del -= new MyDelegate(Oper.Sub);
del(2, 8);

delegate void MyDelegate(int x, int y);

public class Oper
{
    public static void Add(int x, int y)
    {
        Console.WriteLine("{0} + {1} = {2}", x, y, x + y);
    }

    public static void Sub(int x, int y)
    {
        Console.WriteLine("{0} - {1} = {2}", x, y, x - y);
    }
}

这是多播委托的示例。

var del = new MyDelegate(Oper.Add);

我们创建了一个委托实例。委托指向Oper类的静态Add方法。

del += new MyDelegate(Oper.Sub);
del(6, 4);

我们将另一个方法插入到现有的委托实例中。委托的第一次调用会调用两个方法。

del -= new MyDelegate(Oper.Sub);
del(2, 8);

我们从委托中删除了一个方法。委托的第二次调用仅调用一个方法。

delegate void MyDelegate(int x, int y);

我们的委托有两个参数。我们有一个Oper类,它有两个静态方法。一个将两个值相加,另一个将两个值相减。

$ dotnet run
6 + 4 = 10
6 - 4 = 2
2 + 8 = 10

C#匿名方法

委托可以使用匿名方法。

MyDelegate del = delegate
{
    Console.WriteLine("Anonymous method");
};

del();

delegate void MyDelegate();

当使用带有委托的匿名方法时,我们可以省略方法声明。该方法没有名称,只能通过委托调用。

MyDelegate del = delegate
{
    Console.WriteLine("Anonymous method");
};

这里我们创建了一个指向匿名方法的委托。匿名方法的主体由{}字符包围,但没有名称。

C#委托作为方法参数

委托可以用作方法参数。

DoOperation(10, 2, Multiply);
DoOperation(10, 2, Divide);

void DoOperation(int x, int y, Arithm del)
{
    int z = del(x, y);
    Console.WriteLine(z);
}

int Multiply(int x, int y)
{
    return x * y;
}

int Divide(int x, int y)
{
    return x / y;
}

delegate int Arithm(int x, int y);

我们有一个DoOperation方法,它接受一个委托作为参数。

DoOperation(10, 2, Multiply);
DoOperation(10, 2, Divide);

我们调用DoOperation方法。我们向它传递两个值和一个方法。我们如何处理这两个值取决于我们传递的方法。这就是使用委托带来的灵活性。

void DoOperation(int x, int y, Arithm del)
{
    int z = del(x, y);
    Console.WriteLine(z);
}

这是DoOperation方法的实现。第三个参数是委托。DoOperation方法调用作为第三个参数传递给它的方法。

delegate int Arithm(int x, int y);

这是一个委托声明。

$ dotnet run
20
5

C#事件

事件是由某些操作触发的消息。单击按钮或时钟的滴答声就是这样的动作。触发事件的对象称为发送者,接收事件的对象称为接收者。

按照惯例,.NET中的事件委托有两个参数:引发事件的源和事件的数据。

var fe = new FEvent();
fe.FiveEvent += new OnFiveHandler(Callback);

var random = new Random();

for (int i = 0; i < 10; i++)
{
    int rn = random.Next(6);

    Console.WriteLine(rn);

    if (rn == 5)
    {
        fe.OnFiveEvent();
    }
}

void Callback(object sender, EventArgs e)
{
    Console.WriteLine("Five Event occurred");
}

class FEvent
{
    public event OnFiveHandler FiveEvent;

    public void OnFiveEvent()
    {
        if (FiveEvent != null)
        {
            FiveEvent(this, EventArgs.Empty);
        }
    }
}

public delegate void OnFiveHandler(object sender, EventArgs e);

我们有一个创建和启动事件的简单示例。生成一个随机数。如果数字等于5,则生成一个FiveEvent事件。

fe.FiveEvent += new OnFiveHandler(Callback);

在这里,我们将名为FiveEvent的事件插入到Callback方法中。换句话说,如果触发了ValueFive事件,则执行Callback方法。

public event OnFiveHandler FiveEvent;

事件是用event关键字声明的。

public void OnFiveEvent()
{
    if(FiveEvent != null)
    {
        FiveEvent(this, EventArgs.Empty);
    }
}

当随机数等于5时,我们调用OnFiveEvent方法。在此方法中,我们引发了FiveEvent事件。此事件不带任何参数。

$ dotnet run
1
1
5
Five Event occurred
1
1
4
1
2
4
5
Five Event occurred

C#复杂事件示例

接下来我们有一个更复杂的例子。这次我们发送一些数据和生成的事件。

namespace ComplexEvent;

public delegate void OnFiveHandler(object sender, FiveEventArgs e);

public class FiveEventArgs : EventArgs
{
    public int count;
    public DateTime time;

    public FiveEventArgs(int count, DateTime time)
    {
        this.count = count;
        this.time = time;
    }
}

public class FEvent
{
    public event OnFiveHandler FiveEvent;

    public void OnFiveEvent(FiveEventArgs e)
    {
        FiveEvent(this, e);
    }
}

public class RandomEventGenerator
{
    public void Generate()
    {
        int count = 0;
        FiveEventArgs args;

        var fe = new FEvent();
        fe.FiveEvent += new OnFiveHandler(Callback);

        var random = new Random();

        for (int i = 0; i < 10; i++)
        {
            int rn = random.Next(6);

            Console.WriteLine(rn);

            if (rn == 5)
            {
                count++;
                args = new FiveEventArgs(count, DateTime.Now);
                fe.OnFiveEvent(args);
            }
        }
    }

    public void Callback(object sender, FiveEventArgs e)
    {
        Console.WriteLine("Five event {0} occurred at {1}", e.count, e.time);
    }
}

class Program
{
    static void Main()
    {
        var reg = new RandomEventGenerator();
        reg.Generate();
    }
}

我们有四个班级。FiveEventArgs携带事件对象的一些数据。FEvent类封装了事件对象。RandomEventGenerator类负责随机数的生成,是事件的发送者。最后,ComplexEvent是主应用程序类。

public class FiveEventArgs : EventArgs
{
    public int count;
    public DateTime time;
...

FiveEventArgs在事件对象中携带数据。它继承自EventArgs基类。count和time成员是将被初始化并随事件携带的数据。

if (rn == 5)
{
    count++;
    args = new FiveEventArgs(count, DateTime.Now);
    fe.OnFiveEvent(args);
}

如果生成的随机数等于5,我们将使用当前计数和DateTime值实例化FiveEventArgs类。count变量计算此事件生成的次数。DateTime值保存事件生成的时间。

$ dotnet run
2
1
0
5
Five event 1 occurred at 1/7/2022 1:16:03 PM
1
3
1
1
0
3

C#预定义委托

.NET有几个内置的委托,可以减少所需的输入并使开发人员的编程更容易。

C#函数委托

函数

是内置的通用委托类型。

函数

可以与方法、匿名方法或lambda表达式一起使用。

Func可以包含0到16个输入参数,并且必须有一个返回类型。(Func委托有16个重载。)

public delegate TResult Func<in T, out TResult>(T arg);

例如,此委托封装了一个方法,该方法具有一个参数并返回由TResult参数指定的类型的值。

string GetMessage()
{
    return "Hello there!";
}

Func<string> sayHello = GetMessage;

Console.WriteLine(sayHello());

在示例中,我们使用Func委托,它没有参数并返回单个值。

$ dotnet run
Hello there!

C#动作委托

actiondelegate封装了一个没有参数且不返回值的方法。

Action act = ShowMessage;
act();

void ShowMessage()
{
    Console.WriteLine("C# language");
}

使用预定义的委托进一步简化了编程。我们不需要声明委托类型。

Action act = ShowMessage;
act();

我们实例化一个动作委托。委托指向ShowMessage方法。调用委托时,将执行ShowMessage方法。

有多种类型的动作委托。例如,Action委托封装了一个采用单个参数且不返回值的方法。

Action<string> act = ShowMessage;
act("C# language");

void ShowMessage(string message)
{
    Console.WriteLine(message);
}

我们修改前面的示例以使用带有一个参数的动作委托。

Action<string> act = ShowMessage;
act("C# language");

我们创建了一个Action委托实例并使用一个参数调用它。

C#谓词委托

谓词是一种返回真或假的方法。谓词委托是对谓词的引用。谓词对于过滤值列表非常有用。

List<int> vals = new List<int> { 4, 2, 3, 0, 6, 7, 1, 9 };

Predicate<int> myPred = greaterThanThree;

List<int> vals2 = vals.FindAll(myPred);

foreach (int i in vals2)
{
    Console.WriteLine(i);
}

bool greaterThanThree(int x)
{
    return x > 3;
}

我们有一个整数值列表。我们要过滤所有大于三的数字。为此,我们使用谓词委托。

List<int> vals = new List<int> { 4, 2, 3, 0, 6, 7, 1, 9 };

这是整数值的通用列表。

Predicate<int> myPred = greaterThanThree;

我们创建谓词委托的实例。委托指向谓词,一种返回true或false的特殊方法。

List<int> vals2 = vals.FindAll(myPred);

FindAll方法检索与指定谓词定义的条件匹配的所有元素。

bool greaterThanThree(int x)
{
    return x > 3;
}

谓词对所有大于三的值返回真。

在本文中,我们使用了C#中的委托。

列出所有C#教程。

未经允许不得转载:我爱分享网 » C#委托

感觉很棒!可以赞赏支持我哟~

赞(0) 打赏