使用委托(或匿名函数)求一个未知类型的数组最大值

本文展示了如何使用委托来解决求不确定类型数组中最大值的问题,包括字符串数组、字符串类型的数字数组和整型数组的实现。

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

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


namespace DelegateGetMax
{
    delegate int DelMax(object o1, object o2);
    class Program
    {
        /// <summary>
        /// 使用委托求一个不确定类型数组的最大值
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            ////实用委托求字符串数组的最大值
            //object[] str = { "2354tqwafasg", "asgtsa345346t", "w3r3wrsdat4t", "sdgq4w4t46tsdf", "asdgfaweestgwe3", "dagerwet245twreq", "sdfasdfasdfd" };
            //object result = GetMax(str, delegate(object o1, object o2)
            //{
            //    string s1 = (string)o1;
            //    string s2 = (string)o2;
            //    return s1.Length - s2.Length;
            //});
            //Console.WriteLine(result);


            //实用匿名函数求字符串类型的数字求最大值
            //string[] str = { "2354tqwafasg", "asgtsa345346t", "w3r3wrsdat4t", "sdgq4w4t46tsdf", "asdgfaweestgwe3", "dagerwet245twreq", "sdfasdfasdfd" };
            //object result = GetMax(str, (object o1, object o2) =>
            //{
            //    string s1 = (string)o1;
            //    string s2 = (string)o2;
            //    return s1.Length - s2.Length;
            //});
            //Console.WriteLine(result);


            //使用委托求int类型的数组求最大值
            //object[] i = { 124, 15245, 123, 36, 364, 74, 325, 743, 234 };
            //object result = GetMax(i, delegate(object o1, object o2)
            //{
            //    int i1 = (int)o1;
            //    int i2 = (int)o2;
            //    return i1 - i2;
            //});
            //Console.WriteLine(result);


            //实用匿名函数求int类型的数字求最大值
            object[] i = { 124, 15245, 123, 36, 364, 74, 325, 743, 234 };
            object result = GetMax(i, (object o1, object o2) =>
            {
                int i1 = (int)o1;
                int i2 = (int)o2;
                return i1 - i2;
            });
            Console.WriteLine(result);


            Console.ReadKey();
        }
        public static object GetMax(object[] o, DelMax del)
        {
            object max = o[0];
            for (int i = 0; i < o.Length; i++)
            {
                if (del(max, o[i]) < 0)
                {
                    max = o[i];
                }
            }
            return max;
        }
    }
}
<think>Lambda表达式在C#中是一种简洁的匿名函数表示方式,它允许我们以更少的代码来创建委托表达式树。Lambda表达式的主要作用是简化代码,提高可读性,并支持函数式编程风格。以下是Lambda表达式的几个关键作用: 1. **简化匿名方法**:Lambda表达式可以替代匿名方法,使代码更简洁。 2. **支持LINQ查询**:Lambda表达式是LINQ查询操作的基础,用于定义查询条件、投影等。 3. **创建委托实例**:Lambda表达式可以隐式转换为委托类型。 4. **表达式树**:Lambda表达式可以转换为表达式树(Expression Tree),用于在运行时分析代码结构(例如在Entity Framework中转换为SQL查询)。 ### Lambda表达式的语法 Lambda表达式的基本语法有两种形式: 1. **表达式Lambda**:当Lambda体只有一条表达式使用。 ``` (input-parameters) => expression ``` 2. **语句Lambda**:当Lambda体包含多条语句时使用(用大括号包围)。 ``` (input-parameters) => { statement; } ``` ### 示例代码 以下代码展示了Lambda表达式的多种使用场景: ```csharp using System; using System.Collections.Generic; using System.Linq; class LambdaExample { delegate int MathOperation(int x, int y); static void Main() { // 1. 使用Lambda表达式创建委托实例 MathOperation add = (a, b) => a + b; Console.WriteLine($"5 + 3 = {add(5, 3)}"); // 2. 使用Lambda表达式作为参数(例如在List.Find方法中) List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 }; List<int> evenNumbers = numbers.FindAll(n => n % 2 == 0); Console.WriteLine("偶数: " + string.Join(", ", evenNumbers)); // 3. 在LINQ查询中使用Lambda表达式 var squares = numbers.Select(x => x * x); Console.WriteLine("平方: " + string.Join(", ", squares)); // 4. 多参数Lambda表达式 Func<int, int, int> multiply = (x, y) => x * y; Console.WriteLine($"3 * 4 = {multiply(3, 4)}"); // 5. 无参数Lambda表达式 Action greet = () => Console.WriteLine("Hello, Lambda!"); greet(); // 6. 语句Lambda(多条语句) Action<string> log = message => { string logMessage = $"[{DateTime.Now}] {message}"; Console.WriteLine(logMessage); }; log("这是一个日志消息"); // 7. Lambda表达式闭包(捕获外部变量) int factor = 3; Func<int, int> multiplier = n => n * factor; Console.WriteLine($"3 * 5 = {multiplier(5)}"); // 修改外部变量,Lambda会看到改变 factor = 5; Console.WriteLine($"5 * 5 = {multiplier(5)}"); // 8. 在事件处理中使用Lambda表达式 var button = new Button(); button.Click += (sender, e) => Console.WriteLine("按钮被点击了!"); button.SimulateClick(); // 9. 表达式树 System.Linq.Expressions.Expression<Func<int, int, int>> expression = (a, b) => a + b; var compiled = expression.Compile(); Console.WriteLine($"表达式树: 2 + 3 = {compiled(2, 3)}"); } } // 模拟按钮类 class Button { public event EventHandler Click; public void SimulateClick() { Click?.Invoke(this, EventArgs.Empty); } } ``` ### 关键特性解释 1. **类型推断**:Lambda表达式中的参数类型通常可以由编译器推断,因此不需要显式声明类型。 2. **闭包**:Lambda表达式可以捕获外部变量,形成闭包。被捕获的变量的生命周期会延长至与委托相同。 3. **表达式树**:当Lambda表达式赋值给`Expression<TDelegate>`类型时,编译器会生成表达式树而非可执行代码。表达式树可以在运行时被解析,常用于LINQ to SQL等场景。 ### 注意事项 - **变量捕获**:在循环中使用Lambda表达式捕获循环变量时需要注意,因为捕获的是变量本身而不是循环迭代时的值。这可能导致意外的结果(通常需要创建循环内的局部变量副本)。 - **性能**:对于性能敏感的场景,注意Lambda表达式创建的开销(尤其是涉及闭包时,因为闭包会生成一个类来保存捕获的变量)。 - **可读性**:虽然Lambda表达式可以使代码更简洁,但过度使用复杂的Lambda表达式可能降低可读性。 ### 闭包陷阱示例 ```csharp var actions = new List<Action>(); for (int i = 0; i < 3; i++) { // 错误:所有委托都捕获同一个变量i // actions.Add(() => Console.WriteLine(i)); // 正确:创建局部变量副本 int temp = i; actions.Add(() => Console.WriteLine(temp)); } foreach (var action in actions) { action(); // 输出0,1,2 而不是3,3,3 } ``` ### Lambda表达式与匿名方法的比较 Lambda表达式是匿名方法的进一步简化。例如: ```csharp // 匿名方法 Func<int, int> square = delegate(int x) { return x * x; }; // Lambda表达式 Func<int, int> squareLambda = x => x * x; ``` ### 总结 Lambda表达式是C#中一个强大的特性,它极大地简化了委托的创建和匿名方法的使用,是LINQ和函数式编程风格的基石。通过Lambda表达式,开发者可以编写更简洁、更具表达力的代码。 §§相关问题§§: 1. C#中的闭包是什么?它是如何实现的? 2. 表达式树(Expression Tree)在C#中有什么作用?它与Lambda表达式有什么关系? 3. 如何在多线程环境中安全使用Lambda表达式捕获的变量? 4. Lambda表达式和匿名方法在性能上有何差异?</think># C# 中 Lambda 表达式的作用详解 Lambda 表达式是 C# 中一种简洁的匿名函数表示方式,它极大地简化了委托使用,使代码更加简洁、可读性更强。Lambda 表达式在函数式编程、LINQ 查询、事件处理等场景中发挥着重要作用。 ## Lambda 表达式的主要作用 | 作用 | 描述 | 示例 | |------|------|------| | **简化委托** | 替代匿名方法,简洁表示委托 | `(x, y) => x + y` | | **LINQ 查询** | 作为 LINQ 查询的筛选、投影等操作 | `.Where(x => x > 5)` | | **事件处理** | 简化事件处理程序的编写 | `btn.Click += (s, e) => {...}` | | **函数式编程** | 支持高阶函数和函数组合 | `.Select(x => x * x)` | | **闭包支持** | 捕获外部变量,形成闭包 | `int factor = 2; ... x => x * factor` | | **表达式树** | 构建可解析的表达式树 | `Expression<Func<int, bool>> expr = x => x > 5` | ## Lambda 表达式基础语法 ```csharp // 基本语法 (parameters) => expression // 示例: Func<int, int> square = x => x * x; Console.WriteLine(square(5)); // 输出 25 ``` ## Lambda 表达式类型详解 ### 1. 表达式 Lambda ```csharp using System; public class ExpressionLambdaDemo { public static void Main() { // 1. 无参数 Lambda Action greet = () => Console.WriteLine("Hello, Lambda!"); greet(); // 2. 单参数 Lambda (可省略括号) Func<int, int> square = x => x * x; Console.WriteLine($"5 的平方: {square(5)}"); // 3. 多参数 Lambda Func<int, int, int> add = (a, b) => a + b; Console.WriteLine($"3 + 7 = {add(3, 7)}"); // 4. 显式类型参数 Func<double, double, double> divide = (double x, double y) => x / y; Console.WriteLine($"10 / 3 = {divide(10, 3):F2}"); // 5. 在 LINQ 中使用 int[] numbers = { 1, 2, 3, 4, 5 }; var evenSquares = numbers .Where(n => n % 2 == 0) .Select(n => n * n); Console.WriteLine("\n偶数的平方:"); foreach (var num in evenSquares) { Console.WriteLine(num); } } } ``` ### 2. 语句 Lambda ```csharp using System; public class StatementLambdaDemo { public static void Main() { // 1. 带多条语句的 Lambda Action<string> printDetails = name => { string greeting = $"Hello, {name}!"; Console.WriteLine(greeting); Console.WriteLine($"当前时间: {DateTime.Now:T}"); }; printDetails("Alice"); // 2. 带返回值的语句 Lambda Func<int, int, int> max = (a, b) => { if (a > b) return a; else return b; }; Console.WriteLine($"最大值: {max(25, 17)}"); // 3. 在事件处理中使用 var button = new Button(); button.Click += (sender, e) => { Console.WriteLine("按钮被点击!"); Console.WriteLine($"事件参数: {e}"); }; button.SimulateClick(); // 4. 在异步操作中使用 Func<Task> asyncOperation = async () => { Console.WriteLine("开始异步操作..."); await Task.Delay(1000); Console.WriteLine("异步操作完成!"); }; asyncOperation().Wait(); } } // 模拟按钮类 public class Button { public event EventHandler Click; public void SimulateClick() { Click?.Invoke(this, EventArgs.Empty); } } ``` ## 闭包与变量捕获 Lambda 表达式可以捕获外部变量,形成闭包: ```csharp using System; public class ClosureDemo { public static void Main() { // 1. 捕获局部变量 int baseValue = 10; Func<int, int> addBase = x => x + baseValue; Console.WriteLine($"10 + 5 = {addBase(5)}"); // 修改捕获的变量 baseValue = 20; Console.WriteLine($"20 + 5 = {addBase(5)}"); // 闭包捕获的是变量,不是值 // 2. 循环中的闭包问题 Console.WriteLine("\n循环中的闭包问题:"); var actions = new List<Action>(); for (int i = 0; i < 3; i++) { // 错误方式:所有委托都捕获同一个变量i // actions.Add(() => Console.WriteLine($"值: {i}")); // 正确方式:创建局部副本 int temp = i; actions.Add(() => Console.WriteLine($"值: {temp}")); } foreach (var action in actions) { action(); // 输出 0, 1, 2 } // 3. 闭包在延迟执行中的应用 Console.WriteLine("\n延迟执行:"); var functions = new List<Func<int>>(); for (int i = 0; i < 5; i++) { int current = i; functions.Add(() => current * current); } foreach (var func in functions) { Console.WriteLine(func()); } } } ``` ## Lambda 表达式的高级应用 ### 1. 表达式树 ```csharp using System; using System.Linq.Expressions; public class ExpressionTreeDemo { public static void Main() { // 1. 创建表达式树 Expression<Func<int, int, int>> addExpr = (a, b) => a + b; // 2. 分析表达式树 Console.WriteLine("表达式树结构:"); Console.WriteLine($"节点类型: {addExpr.NodeType}"); Console.WriteLine($"返回类型: {addExpr.ReturnType}"); Console.WriteLine($"主体: {addExpr.Body}"); Console.WriteLine($"参数: {string.Join(", ", addExpr.Parameters)}"); // 3. 编译表达式树为委托 var addFunc = addExpr.Compile(); Console.WriteLine($"3 + 4 = {addFunc(3, 4)}"); // 4. 动态构建表达式树 ParameterExpression paramA = Expression.Parameter(typeof(int), "a"); ParameterExpression paramB = Expression.Parameter(typeof(int), "b"); BinaryExpression addBody = Expression.Add(paramA, paramB); Expression<Func<int, int, int>> dynamicAddExpr = Expression.Lambda<Func<int, int, int>>(addBody, paramA, paramB); var dynamicAddFunc = dynamicAddExpr.Compile(); Console.WriteLine($"8 + 9 = {dynamicAddFunc(8, 9)}"); // 5. 在 LINQ to SQL 中的应用 // 实际应用中,表达式树会被转换为 SQL 查询 // var query = db.Products.Where(p => p.Price > 100); } } ``` ### 2. 高阶函数 ```csharp using System; public class HigherOrderFunctions { public static void Main() { // 1. 函数作为参数 int[] numbers = { 1, 2, 3, 4, 5 }; // 应用函数到每个元素 ProcessNumbers(numbers, x => x * x, "平方"); ProcessNumbers(numbers, x => x * 2, "双倍"); ProcessNumbers(numbers, Factorial, "阶乘"); // 2. 函数作为返回值 Func<int, Func<int, int>> createMultiplier = factor => x => x * factor; var doubleFunc = createMultiplier(2); var tripleFunc = createMultiplier(3); Console.WriteLine($"双倍 5: {doubleFunc(5)}"); Console.WriteLine($"三倍 5: {tripleFunc(5)}"); // 3. 函数组合 Func<int, int> square = x => x * x; Func<int, int> increment = x => x + 1; Func<int, int> squareThenIncrement = x => increment(square(x)); Func<int, int> incrementThenSquare = x => square(increment(x)); Console.WriteLine($"先平方后加1: {squareThenIncrement(4)}"); // 16 + 1 = 17 Console.WriteLine($"先加1后平方: {incrementThenSquare(4)}"); // (4+1)^2 = 25 } // 高阶函数示例 static void ProcessNumbers(int[] numbers, Func<int, int> processor, string operationName) { Console.WriteLine($"\n{operationName}操作:"); foreach (int num in numbers) { Console.WriteLine($"{num} -> {processor(num)}"); } } static int Factorial(int n) { if (n <= 1) return 1; return n * Factorial(n - 1); } } ``` ### 3. 模式匹配与 Lambda ```csharp using System; public class PatternMatchingDemo { public static void Main() { object[] items = { 42, "Hello", 3.14, null, new int[] { 1, 2, 3 } }; foreach (var item in items) { // 使用 switch 表达式和 Lambda string description = item switch { int i => $"整数: {i}", string s => $"字符串: {s}", double d => $"双精度浮点数: {d:F2}", null => "空引用", Array a => $"数组,长度: {a.Length}", _ => "未知类型" }; Console.WriteLine(description); } // 使用 when 子句 int[] values = { 10, 20, 30, 40, 50 }; foreach (int value in values) { string result = value switch { < 20 => "小于20", >= 20 and < 40 => "20到39之间", >= 40 => "40更大" }; Console.WriteLine($"{value}: {result}"); } } } ``` ## Lambda 表达式最佳实践 1. **保持简洁性** ```csharp // 简洁 var squares = numbers.Select(n => n * n); // 复杂时使用命名方法 var results = data.Where(IsValidData); private bool IsValidData(DataItem item) { // 复杂的验证逻辑 } ``` 2. **避免修改外部状态** ```csharp // 不良实践:修改外部变量 int counter = 0; items.ForEach(item => { Process(item); counter++; // 副作用 }); // 良好实践:无副作用 int processedCount = items.Count(item => { Process(item); return true; }); ``` 3. **正确处理闭包** ```csharp // 避免循环闭包陷阱 for (int i = 0; i < 10; i++) { int current = i; // 创建局部副本 tasks.Add(Task.Run(() => Console.WriteLine(current))); } ``` 4. **性能考虑** ```csharp // 高频调用的地方避免重复创建委托 private static readonly Func<int, int> Square = x => x * x; void ProcessData(IEnumerable<int> data) { var results = data.Select(Square); // 重用委托 } ``` 5. **表达式树与委托选择** ```csharp // 需要编译时:使用委托 Func<int, int> square = x => x * x; // 需要分析表达式结构时:使用表达式树 Expression<Func<int, int>> squareExpr = x => x * x; ``` ## Lambda 表达式常见陷阱 1. **闭包捕获变量而非值** ```csharp int value = 10; Func<int> getValue = () => value; value = 20; Console.WriteLine(getValue()); // 输出 20,不是 10 ``` 2. **循环变量捕获问题** ```csharp var actions = new List<Action>(); for (int i = 0; i < 3; i++) { // 所有委托捕获同一个变量 i actions.Add(() => Console.WriteLine(i)); } foreach (var action in actions) { action(); // 全部输出 3 } ``` 3. **内存泄漏** ```csharp eventSource.Event += (s, e) => { // 如果 eventSource 生命周期长,可能造成内存泄漏 HandleEvent(e); }; // 解决方案:使用弱引用显式取消订阅 ``` 4. **异步 Lambda 中的异常处理** ```csharp // 错误:异步 Lambda 中的异常可能被忽略 button.Click += async (s, e) => { await SomeAsyncOperation(); }; // 正确:添加异常处理 button.Click += async (s, e) => { try { await SomeAsyncOperation(); } catch (Exception ex) { // 处理异常 } }; ``` ## Lambda 表达式总结 | 特性 | 描述 | 使用场景 | |------|------|----------| | **简洁性** | 减少代码量,提高可读性 | 简单委托实现 | | **闭包** | 捕获外部变量 | 状态保持、回调函数 | | **LINQ 集成** | 查询操作的核心 | 数据筛选、转换、聚合 | | **函数式编程** | 支持高阶函数 | 函数组合、策略模式 | | **表达式树** | 可分析代码结构 | ORM 查询、动态代码生成 | | **事件处理** | 简化事件订阅 | UI 事件、异步回调 | Lambda 表达式是现代 C# 编程的核心特性之一,它使代码更加简洁、表达力更强。掌握 Lambda 表达式的各种用法和注意事项,能够显著提高开发效率和代码质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值