C# delegate初识

本文深入探讨C#中的委托概念,包括Func<T>和Action<T>委托的语法与使用,通过实例展示如何利用委托编写更优雅的代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文链接:https://code-maze.com/csharp-delegates/

在本文中,我们将更多地讨论C#中的委托。

 

委托是对方法的引用。我们可以使用委托对象将它传递给我们想要调用引用方法的代码,而不需要在编译时知道将调用哪个方法。

本文是该系列的一部分

 

如果您想查看本教程的完整导航,可以在此处执行C#Intermediate Tutorial。

要下载源代码,您可以访问C#源代码中的代理。 

我们将把这篇文章分为以下几节:

委托语法

创建委托对象的基本语法是:

	
delegate Result_Type identifier([parameters]);


定义和使用委托有三个步骤:

  • 申明委托
  • 实例化,创建委托的对象
  • 调用,我们称之为引用方法
//Declaration
public delegate void WriterDelegate(string text);
class Program
{
    public static void Write(string text)
    {
        Console.WriteLine(text);
    }
 
    static void Main(string[] args)
    {
        //Instantiation
        WriterDelegate writerDelegate = new WriterDelegate(Write);
 
        //Invocation
        writerDelegate("Some example text.");
    }
}


重要的是要理解方法的返回类型和参数的数量必须匹配委托的返回类型和参数的数量。否则,我们将得到编译器错误。我们可以在我们的示例中看到,我们的Write方法有一个void作为返回类型,只有一个字符串参数。

 

委托在封装我们的方法时非常有用。

C#有两个内置委托:Func<T>、Action<T>,它们被广泛使用,所以让我们来谈谈它们。

Func <T>委托

此委托封装了一个方法,该方法最多包含16个参数并返回指定类型的值。因此,换句话说,我们Func仅使用具有除void之外的返回类型的方法的委托。

我们可以Func使用以下语法实例化委托:

Func<Type1, Type2..., ReturnType> DelegateName = new Func<Type1, Type2..., ReturnType>(MethodName);

我们可以看到方括号内的最后一个参数是返回类型。当然,我们不必像这样初始化委托对象,我们可以用另一种方式来做:

Func< Type1, Type2..., ReturnType> name = MethodName;

 让我们看一下如何使用Func委托示例:

class Program
{
    public static int Sum(int a, int b)
    {
        return a + b;
    }
 
    static void Main(string[] args)
    {
        Func<int, int, int> sumDelegate = Sum;
        Console.WriteLine(sumDelegate(10, 20));
    }
}

Action<T> Delegate

此委托封装了一个方法,该方法最多包含16个参数,并且不返回任何结果。因此,我们只能为此委托分配void返回类型的方法。

 

我们可以使用以下语法实例化Action委托对象:

Action<Type1, Type2...> DelegateName = new Action<Type1, Type2...>(MethodName);

或者,我们可以使用另一种方式:

	
Action < Type1, Type2...> DelegateName = MethodName;

让我们看一下如何使用Action委托示例:

public static void Write(string text)
{
    Console.WriteLine(text);
}
 
static void Main(string[] args)
{
    Action<string> writeDelegate = Write;
    writeDelegate("String parameter to write.");
}

实际例子

在这个例子中,我们将创建一个应用程序,它根据一个提供的参数执行三种方法之一(Sum,Subtract,Multiply)。基本上,如果我们Sum作为参数发送,Sum方法将被执行,依此类推。首先,我们将在没有委托的情况下编写此示例,然后我们将通过引入委托来重构该代码。

那么让我们从第一部分开始:

public enum Operation
{
    Sum,
    Subtract,
    Multiply
}
 
public class OperationManager
{
    private int _first;
    private int _second;
    public OperationManager(int first, int second)
    {
        _first = first;
        _second = second;
    }
 
    private int Sum()
    {
        return _first + _second;
    }
 
    private int Subtract()
    {
        return _first - _second;
    }
 
    private int Multiply()
    {
        return _first * _second;
    }
 
    public int Execute(Operation operation)
    {
        switch (operation)
        {
            case Operation.Sum:
                return Sum();
            case Operation.Subtract:
                return Subtract();
            case Operation.Multiply:
                return Multiply();
            default:
                return -1; //just to simulate
        }
    }
}
 
class Program
{
    static void Main(string[] args)
    {
        var opManager = new OperationManager(20, 10);
        var result = opManager.Execute(Operation.Sum);
        Console.WriteLine($"The result of the operation is {result}"); //使用内插字符串
 
        Console.ReadKey();
    }
}

如果我们启动此应用程序,我们将为我们发送给该Execute方法的任何操作获得正确的响应。但是这段代码可以更好,更容易阅读而无需switch-case表达。如果我们将要进行十次以上的操作(例如),那么这个switch块的读取和维护也会非常难看。

所以,让我们改变我们的代码,使其可读,可维护,更面向对象。我们来介绍一个新类ExecutionManager

public class ExecutionManager
{
    public Dictionary<Operation, Func<int>> FuncExecute { get; set; }
    private Func<int> _sum;
    private Func<int> _subtract;
    private Func<int> _multiply;
 
    public ExecutionManager()
    {
        FuncExecute = new Dictionary<Operation, Func<int>>(3);
    }
 
    public void PopulateFunctions(Func<int> Sum, Func<int> Subtract, Func<int> Multiply)
    {
        _sum = Sum;
        _subtract = Subtract;
        _multiply = Multiply;
    }
 
    public void PrepareExecution()
    {
        FuncExecute.Add(Operation.Sum, _sum);
        FuncExecute.Add(Operation.Subtract, _subtract);
        FuncExecute.Add(Operation.Multiply, _multiply);
    }
}

在这里,我们创建一个字典,它将保存所有操作和所有对我们的方法的引用(Func委托)。现在我们可以将这个类注入到OperationManager类中并更改Execute方法:

public class OperationManager
{
    private int _first;
    private int _second;
    private readonly ExecutionManager _executionManager;
 
    public OperationManager(int first, int second, ExecutionManager executionManager)
    {
        _first = first;
        _second = second;
        _executionManager = executionManager;
        _executionManager.PopulateFunctions(Sum, Subtract, Multiply);
        _executionManager.PrepareExecution();
    }
 
    private int Sum()
    {
        return _first + _second;
    }
 
    private int Subtract()
    {
        return _first - _second;
    }
 
    private int Multiply()
    {
        return _first * _second;
    }
 
    public int Execute(Operation operation)
    {
        return _executionManager.FuncExecute.ContainsKey(operation) ?
            _executionManager.FuncExecute[operation]() :
            -1;
    }
}

最后,我们需要更改Program类:现在,我们在OperationManager类的构造函数中配置,并在Execute方法中执行我们的操作(如果它包含所需的操作)。初看起来,我们可以看到这段代码有多好。

class Program
{
    static void Main(string[] args)
    {
        var executionManager = new ExecutionManager();
        var opManager = new OperationManager(20, 10, executionManager);
        var result = opManager.Execute(Operation.Sum);
        Console.WriteLine($"The result of the operation is {result}");
 
        Console.ReadKey();
    }
}

 

结论

在本文中,我们了解到:

  • 如何实例化委托
  • 使用Func和Action委托的方法
  • 如何使用委托编写更好的代码

其他

1.在stackoverflow上面也有类似的话题:https://stackoverflow.com/questions/1295358/best-practices-when-should-i-use-a-delegate-in-net 可以参考学些

2.委托与Lambda表达式(C#3.0称之为Lambda表达式),事件机制都有密切的关系,后期会再整理相关文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值