跳至页脚内容
Iron Academy Logo
学习 C#
学习 C#

其他类别

了解 C# 委托

Tim Corey
1h 9m 11s

C# 中的委托是一项强大的功能,但许多开发人员并不熟悉如何有效地使用它们。 Tim Corey 的视频" C# 中的委托 - 实际演示,包括 Action 和 Func "对委托是什么、如何使用它们以及为什么它们有用进行了详尽的解释。

本文将为您提供 Tim 关于 C# 中委托的专家见解,清晰地解释其用法和实际应用。 您将学习委托如何增强代码的灵活性和效率,例如在购物车系统中使用委托的示例。

简介

Tim 介绍了委托的概念,强调了委托在 C# 中的强大功能和多功能性。 他向观众保证,尽管有些术语令人望而生畏,但代表制的基本原理很简单。 Tim 旨在揭开委托的神秘面纱,并介绍 func 和 action 等特殊类型。

应用程序演示

Tim 设置了一个演示应用程序来说明委托的用法。 该解决方案包含三个项目:控制台 UI、演示库和 WinForm UI。 初期重点是控制台用户界面和演示库。

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleUI
{
    class Program
    {
        static ShoppingCartModel cart = new ShoppingCartModel();

        static void Main(string[] args)
        {
            PopulateCartWithDemoData();

            Console.WriteLine($"The total for the cart is {cart.GenerateTotal():C2}");

            Console.ReadLine();
        }

        private static void PopulateCartWithDemoData()
        {
            cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
            cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
            cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
            cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
        }
    }
}

public class ShoppingCartModel
{
    public List<ProductModel> Items { get; set; } = new List<ProductModel>();

    public decimal GenerateTotal()
    {
        decimal subtotal = Items.Sum(x => x.Price);

        if (subtotal > 100)
        {
            return subtotal * 0.80M;
        }
        else if (subtotal > 50)
        {
            return subtotal * 0.85M;
        }
        else if (subtotal > 10)
        {
            return subtotal * 0.90M;
        }
        else
        {
            return subtotal;
        }
    }
}

public class ProductModel
{
    public string ItemName { get; set; }
    public decimal Price { get; set; }
}
using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleUI
{
    class Program
    {
        static ShoppingCartModel cart = new ShoppingCartModel();

        static void Main(string[] args)
        {
            PopulateCartWithDemoData();

            Console.WriteLine($"The total for the cart is {cart.GenerateTotal():C2}");

            Console.ReadLine();
        }

        private static void PopulateCartWithDemoData()
        {
            cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
            cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
            cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
            cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
        }
    }
}

public class ShoppingCartModel
{
    public List<ProductModel> Items { get; set; } = new List<ProductModel>();

    public decimal GenerateTotal()
    {
        decimal subtotal = Items.Sum(x => x.Price);

        if (subtotal > 100)
        {
            return subtotal * 0.80M;
        }
        else if (subtotal > 50)
        {
            return subtotal * 0.85M;
        }
        else if (subtotal > 10)
        {
            return subtotal * 0.90M;
        }
        else
        {
            return subtotal;
        }
    }
}

public class ProductModel
{
    public string ItemName { get; set; }
    public decimal Price { get; set; }
}

Tim 解释了演示应用程序的结构和功能:

*购物车模型:表示购物车,其中包含商品列表(ProductModel),并根据小计计算折扣后的总成本。

*产品模型:表示具有名称和价格属性的单个商品。

*控制台应用程序:用演示数据填充购物车,计算总价,并显示总价。

了解折扣

Tim 详细讲解了 GenerateTotal 方法中的折扣逻辑,解释了小计如何决定应用的折扣:

  • 小计超过 100 美元,可享 20% 折扣。
  • 小计超过 50 美元,可享 15% 折扣。
  • 小计超过 10 美元,可享 10% 折扣。
  • 小计金额低于 10 美元不享受折扣。

Tim 使用断点来演示计算和折扣逻辑,确保观众在向代表介绍之前理解基础知识。

解释和创建代表

在本节中,Tim Corey 将深入探讨 C# 中的委托概念,解释其工作原理,并通过实际代码示例演示其用法。

什么是代表?

Tim解释说,委托本质上是一种将方法作为参数传递的方式。 与其传递变量或属性,不如传递方法,这样可以编写更灵活、更可重用的代码。

创建和使用委托

以下是 Tim 对创建和使用委托过程的详细讲解:

1.定义委托

* 委托定义在类的顶部,指定返回类型和参数类型。

```csharp
public delegate void MentionDiscount(decimal subtotal);
```

* 此委托指定一个返回 void 的方法,该方法接受一个十进制数作为参数。

2.在方法中使用委托

* 该委托对象用作 ShoppingCartModel 类的 GenerateTotal 方法中的参数。

```csharp
public decimal GenerateTotal(MentionDiscount mentionDiscount)
{
    decimal subtotal = Items.Sum(x => x.Price);

    // Call a method passed as a delegate
    mentionDiscount(subtotal);

    if (subtotal > 100)
    {
        return subtotal * 0.80M;
    }
    else if (subtotal > 50)
    {
        return subtotal * 0.85M;
    }
    else if (subtotal > 10)
    {
        return subtotal * 0.90M;
    }
    else
    {
        return subtotal;
    }
}
```

3.创建要传递给委托的方法

* 在 Program 类中创建一个与委托签名匹配的方法。

```csharp
private static void SubtotalAlert(decimal subtotal)
{
    Console.WriteLine($"The subtotal is {subtotal:C2}");
}
```

4.调用 GenerateTotal 方法

* 该方法通过委托传递给 GenerateTotal 方法。

```csharp
class Program
{
    static ShoppingCartModel cart = new ShoppingCartModel();

    static void Main(string[] args)
    {
        PopulateCartWithDemoData();

        Console.WriteLine($"The total for the cart is {cart.GenerateTotal(SubtotalAlert):C2}");

        Console.ReadLine();
    }

    private static void PopulateCartWithDemoData()
    {
        cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
        cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
        cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
        cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
    }
}
```

运行应用程序

Tim 运行该应用程序来演示委托是如何工作的。 控制台输出显示了购物车的小计和总计,表明 SubtotalAlert 方法已通过委托成功传递和执行。

Understanding Csharp Delegate 1 related to 运行应用程序

函数和动作:你可以用委托解决的问题

Tim Corey 随后探讨了 C# 中 func 和 action delegate 的使用。 这些是微软提供的特殊类型的委托,旨在简化泛型委托的使用。

识别问题

Tim 指出了一个常见问题:GenerateTotal 方法中硬编码的折扣逻辑。 这种方法不够灵活,每次折扣规则发生变化时,都需要修改代码、重新编译和重新部署。

public decimal GenerateTotal()
{
    decimal subtotal = Items.Sum(x => x.Price);
    if (subtotal > 100)
    {
        return subtotal * 0.80M;
    }
    else if (subtotal > 50)
    {
        return subtotal * 0.85M;
    }
    else if (subtotal > 10)
    {
        return subtotal * 0.90M;
    }
    else
    {
        return subtotal;
    }
}
public decimal GenerateTotal()
{
    decimal subtotal = Items.Sum(x => x.Price);
    if (subtotal > 100)
    {
        return subtotal * 0.80M;
    }
    else if (subtotal > 50)
    {
        return subtotal * 0.85M;
    }
    else if (subtotal > 10)
    {
        return subtotal * 0.90M;
    }
    else
    {
        return subtotal;
    }
}

介绍 Func 委托

Tim 引入了 func delegate 来解决硬编码折扣问题。 func 委托是一个通用委托,它表示一个方法签名,带有返回类型和最多 16 个输入参数。

1.定义函数委托

* 函数委托在 GenerateTotal 方法中使用,以动态处理折扣计算。

```csharp
public decimal GenerateTotal(Func<List<ProductModel>, decimal, decimal> calculateDiscountedTotal)
{
    decimal subtotal = Items.Sum(x => x.Price);
    MentionDiscount(subtotal);

    return calculateDiscountedTotal(Items, subtotal);
}
```

2.创建折扣计算方法

* 在 Program 类中创建一个与函数委托签名匹配的方法。

```csharp
private static decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
    if (subtotal > 100)
    {
        return subtotal * 0.80M;
    }
    else if (subtotal > 50)
    {
        return subtotal * 0.85M;
    }
    else if (subtotal > 10)
    {
        return subtotal * 0.90M;
    }
    else
    {
        return subtotal;
    }
}
```

3.将方法传递给函数委托

* CalculateLevelDiscount 方法通过函数委托传递给 GenerateTotal 方法。

```csharp
class Program
{
    static ShoppingCartModel cart = new ShoppingCartModel();

    static void Main(string[] args)
    {
        PopulateCartWithDemoData();

        Console.WriteLine($"The total for the cart is {cart.GenerateTotal(CalculateLevelDiscount):C2}");

        Console.ReadLine();
    }

    private static void PopulateCartWithDemoData()
    {
        cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
        cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
        cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
        cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
    }

    private static decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
    {
        if (subtotal > 100)
        {
            return subtotal * 0.80M;
        }
        else if (subtotal > 50)
        {
            return subtotal * 0.85M;
        }
        else if (subtotal > 10)
        {
            return subtotal * 0.90M;
        }
        else
        {
            return subtotal;
        }
    }
}
```

运行应用程序

Tim 演示了修改后的应用程序,表明它运行正常,并能根据提供的逻辑动态计算折扣。

Understanding Csharp Delegate 2 related to 运行应用程序

委托和函数之间的区别

Tim 对比了自定义委托和函数委托:

*委托:需要明确定义签名,提供清晰的文档和结构。

*函数:更简洁,但每次都需要指定输入和输出类型,这可能不太清晰。

两种方法都提供了灵活性,但选择取决于具体的使用案例和应用程序的复杂程度。

既然所有工作都在其他地方完成,为什么还要使用委托呢?

Tim Corey 回答了一个关于使用委托的常见问题:如果所有工作似乎都在其他地方完成,为什么还要使用委托呢?

Tim解释说,委托的目的是为代码提供灵活性和可扩展性。 ShoppingCartModel 类中的 GenerateTotal 方法可能不仅仅用于计算折扣。 它还可以处理诸如检查库存可用性、验证购物车内容或其他业务逻辑等任务。 委托允许您传递特定方法以执行特殊任务或自定义行为,而无需更改核心方法。 这使得代码更具模块化,更易于维护。

在以下情况下,委托尤其有用:

  • 动态应用不同的业务规则或逻辑。 保持核心方法的通用性和可重用性。
  • 在不修改核心方法的情况下,针对特定情况实现自定义行为。

行动代表:创建和解释

Tim 介绍了 Action 委托,这是 C# 中的另一种特殊类型的委托。 Action 委托类似于 Func,但它不返回值(即,它返回 void)。

1.创建操作委托

* 在 GenerateTotal 方法中定义 Action 委托,以处理警报或消息。

```csharp
public decimal GenerateTotal(Func<List<ProductModel>, decimal, decimal> calculateDiscountedTotal, Action<string> tellUserWeAreDiscounting)
{
    decimal subtotal = Items.Sum(x => x.Price);
    MentionSubtotal(subtotal);

    tellUserWeAreDiscounting("We are applying your discount.");

    return calculateDiscountedTotal(Items, subtotal);
}
```

2.创建警报方法

* 定义一个与 Action 委托的签名匹配的方法。

```csharp
private static void AlertUser(string message)
{
    Console.WriteLine(message);
}
```

3.将方法传递给操作委托

* 通过 Action 委托将 AlertUser 方法传递给 GenerateTotal 方法。

```csharp
class Program
{
    static ShoppingCartModel cart = new ShoppingCartModel();

    static void Main(string[] args)
    {
        PopulateCartWithDemoData();

        Console.WriteLine($"The total for the cart is {cart.GenerateTotal(CalculateLevelDiscount, AlertUser):C2}");

        Console.ReadLine();
    }

    private static void PopulateCartWithDemoData()
    {
        cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
        cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
        cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
        cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
    }

    private static decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
    {
        if (subtotal > 100)
        {
            return subtotal * 0.80M;
        }
        else if (subtotal > 50)
        {
            return subtotal * 0.85M;
        }
        else if (subtotal > 10)
        {
            return subtotal * 0.90M;
        }
        else
        {
            return subtotal;
        }
    }

    private static void AlertUser(string message)
    {
        Console.WriteLine(message);
    }
}
```

创建匿名方法:匿名委托

Tim 展示了如何使用匿名方法,允许你动态定义方法而无需命名它们。

1.定义匿名方法

* 与其创建命名方法,不如直接在需要的地方定义方法。

```csharp
class Program
{
    static ShoppingCartModel cart = new ShoppingCartModel();

    static void Main(string[] args)
    {
        PopulateCartWithDemoData();

        Console.WriteLine($"The total for the cart is {cart.GenerateTotal((items, subtotal) => 
        {
            if (subtotal > 100) return subtotal * 0.80M;
            else if (subtotal > 50) return subtotal * 0.85M;
            else if (subtotal > 10) return subtotal * 0.90M;
            else return subtotal;
        },
        (message) => Console.WriteLine(message)):C2}");

        Console.ReadLine();
    }

    private static void PopulateCartWithDemoData()
    {
        cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
        cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
        cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
        cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
    }
}
```

2.理解语法

* 匿名方法语法使用`=>`运算符(lambda表达式)直接内联定义方法体。
* 无需指定返回类型或方法名称。

通过使用委托(包括 Func、Action 和匿名方法),开发人员可以创建更动态、模块化的代码,从而实现灵活且可重用的组件。

在其他项目中使用委托:WinForms

在本部分中,Tim Corey 通过将委托的使用扩展到 WinForms 应用程序来演示委托的强大功能。 这突显了委托如何在各种用户界面 (UI) 环境中促进不同的行为。

设置 WinForms 应用程序

1.带有两个按钮的 WinForm 用户界面

* 该表单有两个按钮:一个用于演示消息框,一个用于演示文本框。

* 此外,还包括 ShoppingCartModel 以及用演示数据填充它的方法。
public partial class Dashboard : Form
{
    ShoppingCartModel cart = new ShoppingCartModel();

    public Dashboard()
    {
        InitializeComponent();
        PopulateCartWithDemoData();
    }

    private void PopulateCartWithDemoData()
    {
        cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
        cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
        cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
        cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
    }

    private void messageBoxDemoButton_Click(object sender, EventArgs e)
    {
        decimal total = cart.GenerateTotal(SubtotalAlert, CalculateLevelDiscount, PrintOutDiscountAlert);
        MessageBox.Show($"The total is {total:C2}");
    }

    private void textBoxDemoButton_Click(object sender, EventArgs e)
    {
        // Code for TextBox demo will go here
    }
}
public partial class Dashboard : Form
{
    ShoppingCartModel cart = new ShoppingCartModel();

    public Dashboard()
    {
        InitializeComponent();
        PopulateCartWithDemoData();
    }

    private void PopulateCartWithDemoData()
    {
        cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
        cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
        cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
        cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
    }

    private void messageBoxDemoButton_Click(object sender, EventArgs e)
    {
        decimal total = cart.GenerateTotal(SubtotalAlert, CalculateLevelDiscount, PrintOutDiscountAlert);
        MessageBox.Show($"The total is {total:C2}");
    }

    private void textBoxDemoButton_Click(object sender, EventArgs e)
    {
        // Code for TextBox demo will go here
    }
}

创建委托方法

  1. PrintOutDiscountAlert

    • 此方法将用于显示包含折扣信息的提醒。
    private void PrintOutDiscountAlert(string message)
    {
        MessageBox.Show(message);
    }
    private void PrintOutDiscountAlert(string message)
    {
        MessageBox.Show(message);
    }

2.小计提醒

* 此方法会在消息框中显示小计。

```csharp
private void SubtotalAlert(decimal subtotal)
{
    MessageBox.Show($"The subtotal is {subtotal:C2}");
}
```

3.计算等级折扣

* 此方法将根据购物车中的商品数量计算折扣。

```csharp
private decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
    if (items.Count > 3)
    {
        return subtotal - 3M;
    }
    return subtotal - items.Count;
}
```

将委托与 WinForms 集成

1.在按钮点击事件中使用委托

* `messageBoxDemoButton_Click`方法展示了如何将委托传递给GenerateTotal方法,并使用消息框处理结果。

```csharp
private void messageBoxDemoButton_Click(object sender, EventArgs e)
{
    decimal total = cart.GenerateTotal(SubtotalAlert, CalculateLevelDiscount, PrintOutDiscountAlert);
    MessageBox.Show($"The total is {total:C2}");
}
```

2.运行应用程序

* 当单击按钮时,WinForms 应用程序会使用消息框显示小计和总计,从而展示委托的灵活性。

![](/static-assets/academy/articles/understanding-csharp-delegate/understanding-csharp-delegate-3.webp)

结论

Tim Corey 对 C# 中的委托进行了清晰的解释,涵盖了委托的基础知识、高级用法以及在购物车中使用委托等实际示例。 他展示了委托如何实现灵活且可重用的代码,包括 Func、Action 和匿名方法。 观看完整视频,了解如何在项目中有效地运用委托!

Hero Worlddot related to 了解 C# 委托
Hero Affiliate related to 了解 C# 委托

分享您的所爱,赚取更多收入

您为使用 .NET、C#、Java、Python 或 Node.js 的开发人员创建内容吗?将您的专业知识转化为额外收入!

钢铁支援团队

我们每周 5 天,每天 24 小时在线。
聊天
电子邮件
打电话给我