C#扩展方法与Lambda表达式基本用法

C# 扩展方法与 Lambda 表达式详解

一、扩展方法详解

1. 基本概念

​扩展方法​​允许为现有类型"添加"方法,而无需修改原始类型或创建派生类型。

​定义条件​​:

  • 必须在静态类中定义
  • 方法本身必须是静态的
  • 第一个参数使用this修饰符指定要扩展的类型

​示例​​:

public static class StringExtensions
{// 扩展string类型的方法public static bool IsNullOrEmpty(this string str){return string.IsNullOrEmpty(str);}// 扩展int类型的方法public static bool IsEven(this int number){return number % 2 == 0;}
}// 使用
string s = "test";
Console.WriteLine(s.IsNullOrEmpty()); // Falseint num = 4;
Console.WriteLine(num.IsEven()); // True

2. 高级用法

(1) 扩展泛型方法
public static class EnumerableExtensions
{public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T> source) where T : class{return source.Where(item => item != null);}public static IEnumerable<T?> WhereNotNull<T>(this IEnumerable<T?> source) where T : struct{return source.Where(item => item.HasValue).Select(item => item.Value);}
}// 使用
var strings = new List<string?> { "a", null, "b" };
var nonNullStrings = strings.WhereNotNull(); // 返回 "a", "b"
(2) 扩展索引器
public static class DictionaryExtensions
{public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue = default){return dictionary.TryGetValue(key, out var value) ? value : defaultValue;}
}// 使用
var dict = new Dictionary<string, int> { { "one", 1 }, { "two", 2 } };
Console.WriteLine(dict.GetOrDefault("one")); // 1
Console.WriteLine(dict.GetOrDefault("three", 0)); // 0
(3) 扩展异步方法
public static class AsyncExtensions
{public static async Task<T> WithTimeout<T>(this Task<T> task, TimeSpan timeout){using var timeoutCancellationTokenSource = new CancellationTokenSource();var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));if (completedTask == task){timeoutCancellationTokenSource.Cancel();return await task;}throw new TimeoutException("The operation has timed out.");}
}// 使用
var result = await SomeAsyncOperation().WithTimeout(TimeSpan.FromSeconds(5));

3. 最佳实践

  1. ​命名规范​​:

    • 扩展方法名应清晰表明其功能
    • 避免与现有方法名冲突
  2. ​静态类组织​​:

    • 按功能分组相关扩展方法
    • 考虑按扩展类型命名静态类(如StringExtensions
  3. ​文档注释​​:

    /// <summary>
    /// 检查字符串是否为null或空
    /// </summary>
    /// <param name="str">要检查的字符串</param>
    /// <returns>如果字符串为null或空则返回true</returns>
    public static bool IsNullOrEmpty(this string str)
  4. ​性能考虑​​:

    • 避免在扩展方法中执行昂贵操作
    • 考虑延迟执行(如使用yield return
  5. ​避免过度使用​​:

    • 不应滥用扩展方法来"修复"不良API设计
    • 优先考虑继承或组合等更明确的设计模式

二、Lambda表达式详解

1. 基本语法

​基本形式​​:

(参数列表) => 表达式或语句块

​示例​​:

// 表达式Lambda
Func<int, int> square = x => x * x;// 语句Lambda
Action<string> greet = name => 
{Console.WriteLine($"Hello, {name}!");
};greet("World"); // 输出: Hello, World!

2. 类型推断

编译器可以自动推断Lambda表达式的参数类型:

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = numbers.Where(n => n % 2 == 0); // n自动推断为int

3. 多参数Lambda

Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(3, 5)); // 输出: 8

4. 无参数Lambda

Action sayHello = () => Console.WriteLine("Hello!");
sayHello(); // 输出: Hello!

5. 返回值Lambda

Func<int, int, int> multiply = (x, y) => 
{int result = x * y;return result;
};

6. 复杂Lambda表达式

Func<int, bool> isEven = x => 
{if (x % 2 == 0)return true;elsereturn false;
};

三、扩展方法与Lambda结合使用

1. 在扩展方法中使用Lambda

public static class EnumerableExtensions
{public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, bool condition, Func<T, bool> predicate){return condition ? source.Where(predicate) : source;}
}// 使用
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var result = numbers.WhereIf(true, x => x > 2); // 返回3,4,5

2. 高阶函数返回Lambda

public static class FunctionFactory
{public static Func<int, int> CreateMultiplier(int factor){return x => x * factor;}
}// 使用
var double = FunctionFactory.CreateMultiplier(2);
Console.WriteLine(double(5)); // 输出: 10

3. 延迟执行与Lambda

public static class LazyExtensions
{public static IEnumerable<T> LazySelect<T>(this IEnumerable<T> source, Func<T, T> selector){foreach (var item in source){yield return selector(item);}}
}// 使用
var numbers = new List<int> { 1, 2, 3 };
var lazyNumbers = numbers.LazySelect(x => x * 10); // 不立即执行
foreach (var num in lazyNumbers) // 此时才执行
{Console.WriteLine(num); // 输出: 10, 20, 30
}

四、Lambda表达式的高级特性

1. 表达式树(Expression Trees)

using System.Linq.Expressions;Expression<Func<int, bool>> expr = x => x > 5;
Console.WriteLine(expr); // 输出: x => (x > 5)// 编译表达式
Func<int, bool>> compiled = expr.Compile();
Console.WriteLine(compiled(6)); // 输出: True

2. 捕获外部变量

int factor = 10;
Func<int, int> multiplier = x => x * factor;
Console.WriteLine(multiplier(5)); // 输出: 50

​注意​​:捕获的变量会被提升到闭包中

3. 方法组转换

public class Calculator
{public int Add(int a, int b) => a + b;
}// 方法组转换为委托
Calculator calc = new Calculator();
Func<int, int, int> addDelegate = calc.Add;
Console.WriteLine(addDelegate(3, 4)); // 输出: 7

4. 匿名方法与Lambda对比

// 匿名方法
Func<int, int> square1 = delegate (int x) { return x * x; };// Lambda表达式
Func<int, int> square2 = x => x * x;

​Lambda优势​​:

  • 更简洁的语法
  • 自动类型推断
  • 支持表达式树

五、性能考虑

1. Lambda vs 方法

// 方法
private static int Add(int a, int b) => a + b;// 使用方法
Func<int, int, int> addMethod = Add;// 使用Lambda
Func<int, int, int> addLambda = (a, b) => a + b;

​性能差异​​:

  • 方法调用通常略快(无闭包开销)
  • Lambda在简单情况下与方法性能相当
  • 复杂Lambda可能有轻微性能开销

2. 闭包优化

// 不推荐 - 创建多个闭包
List<Func<int>> funcs = new List<Func<int>>();
for (int i = 0; i < 10; i++)
{funcs.Add(() => i); // 捕获循环变量i
}// 推荐 - 创建局部副本
for (int i = 0; i < 10; i++)
{int copy = i;funcs.Add(() => copy); // 捕获局部变量copy
}

六、调试与测试

1. Lambda调试技巧

// 添加调试信息
Func<int, int> debugLambda = x => 
{Console.WriteLine($"Input: {x}");return x * 2;
};

2. 单元测试Lambda

[Fact]
public void TestLambda()
{// ArrangeFunc<int, int> square = x => x * x;// Actvar result = square(5);// AssertAssert.Equal(25, result);
}

七、常见陷阱与解决方案

1. 变量捕获问题

​问题​​:

var funcs = new List<Func<int>>();
for (int i = 0; i < 3; i++)
{funcs.Add(() => i); // 所有lambda都捕获同一个i
}
// 结果都是3

​解决方案​​:

var funcs = new List<Func<int>>();
for (int i = 0; i < 3; i++)
{int copy = i;funcs.Add(() => copy); // 每个lambda捕获自己的copy
}
// 结果0,1,2

2. 可变性陷阱

​问题​​:

bool flag = false;
Action toggle = () => flag = !flag;
toggle();
Console.WriteLine(flag); // true

​解决方案​​:

// 如果需要不可变行为,使用局部变量
void SetupToggle(out Action toggle)
{bool flag = false;toggle = () => flag = !flag; // 编译错误 - flag必须是final/readonly// 正确做法:使用闭包或重构设计
}

3. 性能陷阱

​问题​​:

// 每次调用都创建新委托
Func<int, int> CreateMultiplier(int factor)
{return x => x * factor; // 每次调用都创建新闭包
}

​解决方案​​:

// 缓存委托
private static readonly Dictionary<int, Func<int, int>> _multipliers = new Dictionary<int, Func<int, int>>();Func<int, int> CreateMultiplier(int factor)
{if (!_multipliers.TryGetValue(factor, out var multiplier)){multiplier = x => x * factor;_multipliers[factor] = multiplier;}return multiplier;
}

八、最佳实践

1. 命名规范

  • 简单Lambda:x => x * 2
  • 复杂Lambda:使用大括号和return
    x => 
    {// 多行逻辑return x * 2;
    }

2. 可读性

  • 保持Lambda简短(通常不超过2-3行)
  • 复杂逻辑提取为命名方法
    Func<int, int> complexCalc = x => CalculateSomething(x);private static int CalculateSomething(int x)
    {// 复杂逻辑return x * 2;
    }

3. 错误处理

Func<string, int> safeParse = s => 
{if (int.TryParse(s, out var result))return result;throw new ArgumentException("Invalid number format");
};

4. 延迟执行

public static IEnumerable<T> Filter<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{foreach (var item in source){if (predicate(item))yield return item;}
}

九、扩展方法与Lambda结合的高级模式

1. 流式API设计

public static class QueryExtensions
{public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, bool condition, Expression<Func<T, bool>> predicate){return condition ? source.Where(predicate) : source;}public static IQueryable<T> OrderByProperty<T>(this IQueryable<T> source, string propertyName){var parameter = Expression.Parameter(typeof(T), "x");var property = Expression.Property(parameter, propertyName);var lambda = Expression.Lambda(property, parameter);var methodName = "OrderBy";var method = typeof(Queryable).GetMethods().First(m => m.Name == methodName && m.GetParameters().Length == 2).MakeGenericMethod(typeof(T), property.Type);return (IQueryable<T>)method.Invoke(null, new object[] { source, lambda });}
}// 使用
var query = dbContext.Products.WhereIf(showActiveOnly, p => p.IsActive).OrderByProperty("Price");

2. 函数组合

public static class FunctionCombinators
{public static Func<T, Z> Compose<T, Y, Z>(this Func<T, Y> f, Func<Y, Z> g){return x => g(f(x));}
}// 使用
Func<int, int> square = x => x * x;
Func<int, int> increment = x => x + 1;
Func<int, int> squareThenIncrement = increment.Compose(square);
Console.WriteLine(squareThenIncrement(3)); // (3 * 3)+1 = 10

3. 惰性求值

public static class LazyExtensions
{public static IEnumerable<T> LazySelect<T>(this IEnumerable<T> source, Func<T, T> selector){foreach (var item in source){yield return selector(item);}}
}// 使用
var numbers = new List<int> { 1, 2, 3 };
var lazyNumbers = numbers.LazySelect(x => 
{Console.WriteLine($"Processing {x}");return x * 10;
});// 此时不会执行
foreach (var num in lazyNumbers) // 执行时才处理
{Console.WriteLine(num);
}

十、实际应用示例

1. 数据库查询构建器

public class QueryBuilder<T>
{private readonly List<Expression<Func<T, bool>>> _predicates = new();public QueryBuilder<T> Where(Expression<Func<T, bool>> predicate){_predicates.Add(predicate);return this;}public IQueryable<T> Build(IQueryable<T> source){IQueryable<T> query = source;foreach (var predicate in _predicates){query = query.Where(predicate);}return query;}
}// 使用
var queryBuilder = new QueryBuilder<Product>().Where(p => p.Price > 100).Where(p => p.Category == "Electronics");var query = queryBuilder.Build(dbContext.Products);

2. 链式验证器

public static class ValidatorExtensions
{public static IValidator<T> Ensure<T>(this IValidator<T> validator, Func<T, bool> condition, string errorMessage){return new ConditionalValidator<T>(validator, condition, errorMessage);}
}public class ConditionalValidator<T> : IValidator<T>
{private readonly IValidator<T> _inner;private readonly Func<T, bool> _condition;private readonly string _errorMessage;public ConditionalValidator(IValidator<T> inner, Func<T, bool> condition, string errorMessage){_inner = inner;_condition = condition;_errorMessage = errorMessage;}public ValidationResult Validate(T instance){var result = _inner.Validate(instance);if (_condition(instance) && !result.IsValid){result.Errors.Add(new ValidationFailure("", _errorMessage));}return result;}
}// 使用
var validator = new BasicValidator<User>().Ensure(u => u.Age < 18, "未成年人不允许注册").Ensure(u => !string.IsNullOrEmpty(u.Email), "邮箱不能为空");

通过合理使用扩展方法和Lambda表达式,可以显著提高C#代码的表达力和简洁性,同时保持良好的可维护性和性能。在实际开发中,应根据具体场景选择最合适的模式和技术。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/81115.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#规避内存泄漏的编码方法

C#规避内存泄漏的编码方法 内存泄漏是C#开发中常见的问题&#xff0c;尽管.NET有垃圾回收机制(GC)&#xff0c;但不当的编码实践仍可能导致内存无法被及时回收。以下是系统性的规避内存泄漏的方法&#xff1a; 一、理解内存泄漏的常见原因 ​​未释放的事件订阅​​​​静态…

React 后台管理系统

这是一个基于 React TypeScript Ant Design 开发的向明天系统前端项目。 git仓库地址 技术栈 React 19TypeScriptAnt Design 5.xRedux ToolkitReact RouterAxiosLess 环境要求 Node.js (推荐使用最新LTS版本)npm 或 yarn 安装步骤 克隆项目到本地 git clone [https://…

第九节:文件操作

理论知识 文件的基本概念&#xff1a;文件是存储数据的基本单位&#xff0c;在 Linux 系统中&#xff0c;一切皆文件。文件可以是文本文件、二进制文件、设备文件等。文件的创建&#xff1a;使用 touch 命令可以创建一个新的空文件。如果文件已经存在&#xff0c;则更新文件的…

2025-03 机器人等级考试四级理论真题 4级

1 2025年蛇年春晚&#xff0c;节目《秧BOT》机器人舞蹈表演节目点燃了全国观众的热情&#xff0c;请问参加节目表演的机器人是由哪家公司研发&#xff1f;&#xff08; &#xff09; A.大疆 B.华为 C.优必选 D.宇树科技 【参考答…

k8s平台:手动部署Grafana

以下是一个可用于生产环境的 Kubernetes 部署 Grafana 的 YAML 文件。该配置包括 Deployment、Service、ConfigMap 和 PersistentVolumeClaim&#xff0c;确保 Grafana 的高可用性和数据持久化。 Grafana 生产部署 YAML 文件 ☆实操示例 cat grafana-deployment.yaml --- # …

农产品园区展示系统——仙盟创梦IDE开发

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>农业大数据平台</title><style>* {margi…

每日Bug:(2)共享内存

对于整个系统而言&#xff0c;主存与CPU的资源都是有限的&#xff0c;随着打开进程数量的增加&#xff0c;若是将所有进程运行所需的代码/数据/栈/共享库都存放在主存中&#xff0c;那么开启一部分进程就可以将主存占用完。 虚拟内存就是解决以上问题的方法&#xff0c;使用虚…

C语言Makefile编写与使用指南

Makefile 详细指南&#xff1a;编写与使用 Makefile 是 C/C 项目中常用的自动化构建工具&#xff0c;它定义了项目的编译规则和依赖关系。下面我将详细介绍 Makefile 的编写和使用方法。 一、Makefile 基础 1. 基本结构 一个典型的 Makefile 包含以下部分&#xff1a; mak…

Centos离线安装Docker(无坑版)

1、下载并上传docker离线安装包 官方地址&#xff1a;安装包下载 2、上传到离线安装的服务器解压 tar -zxvf docker-28.1.1.tgz#拷贝解压二进制文件到相关目录 cp docker/* /usr/bin/ 3、创建docker启动文件 cat << EOF > /usr/lib/systemd/system/docker.servic…

OceanBase数据库-学习笔记4-租户

租户 租户偏向于资源层面的逻辑概念&#xff0c;是在物理节点上划分的资源单元&#xff0c;可以指定其资源规格&#xff0c;包括 CPU、内存、日志盘空间、IOPS 等。 租户类似于传统数据库的数据库实例&#xff0c;租户通过资源池与资源关联&#xff0c;从而独占一定的资源配额…

UNIAPP项目记录

一、通过 vue-cli 创建 uni-app 项目 创建 vue3 项目 创建以 javascript 开发的工程&#xff08;如命令行创建失败&#xff0c;请直接访问 gitee 下载模板&#xff09; npx degit dcloudio/uni-preset-vue#vite my-vue3-project复制代码 npx degit dcloudio/uni-preset-vue#vit…

华为发布全球首个L3商用智驾ADS4.0

2024年10月2024世界智能网联汽车大会上&#xff0c;余承东讲到&#xff1a;“华为ADS 4.0将于2025年推出高速L3级自动驾驶商用及城区L3级自动驾驶试点&#xff0c;希望加快L3级自动驾驶标准的进程&#xff0c;推动L3级自动驾驶技术的普及。” 世界智能网联汽车大会演讲PPT 所以…

【Python学习路线】零基础到项目实战

目录 &#x1f31f; 前言技术背景与价值当前技术痛点解决方案概述目标读者说明 &#x1f9e0; 一、技术原理剖析核心概念图解核心作用讲解关键技术模块说明技术选型对比 &#x1f4bb; 二、实战演示环境配置要求核心代码实现运行结果验证 ⚡ 三、性能对比测试方法论量化数据对比…

解决redis序列号和反序列化问题

最近遇到了一个问题,将 List<Map<String, Object>> 类型数据以list形式存入到redis之后,发现取出来时数据格式完全不对,根据报错信息发现是反序列化问题,遇到类似问题,主要有两种解决方案1.使用序列号工具 例如&#xff0c;Java中常用的序列化工具有Jackson、Gso…

Android学习总结之设计场景题

设计图片请求框架的缓存模块 核心目标是通过分层缓存策略&#xff08;内存缓存 磁盘缓存&#xff09;提升图片加载效率&#xff0c;同时兼顾内存占用和存储性能。以下是针对 Android 面试官的回答思路&#xff0c;结合代码注释说明关键设计点&#xff1a; 一、缓存架构设计&…

Webug3.0通关笔记14 第十四关:存储型XSS

目录 第十四关:存储型XSS 1.打开靶场 2.源码分析 3.渗透实战 第十四关:存储型XSS 本文通过《webug3靶场第十四关 存储型XSS》来进行存储型XSS关卡的渗透实战。 存储型 XSS&#xff08;Stored Cross - Site Scripting&#xff09;&#xff0c;也被称为持久型 XSS&#xff…

Java父类、子类实例初始化顺序详解

1、完整的初始化顺序&#xff08;含继承&#xff09; 1、父类的静态初始化 父类静态变量默认值 → 父类静态变量显式赋值 父类静态代码块&#xff08;按代码顺序执行&#xff09;。 2、子类的静态初始化 子类静态变量默认值 → 子类静态变量显式赋值 子类静态代码块&…

13.组合模式:思考与解读

原文地址:组合模式&#xff1a;思考与解读 更多内容请关注&#xff1a;7.深入思考与解读设计模式 引言 在软件开发中&#xff0c;是否曾经遇到过这样一种情况&#xff1a;你有一个对象&#xff0c;它本身很简单&#xff0c;但是它包含了其他类似的对象。随着系统变得越来越复…

OpenCV实战教程 第一部分:基础入门

第一部分&#xff1a;基础入门 1. OpenCV简介 什么是OpenCV及其应用领域 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和机器学习软件库&#xff0c;于1999年由Intel公司发起&#xff0c;现在由非营利组织OpenCV.org维护。Ope…

虚幻商城 Quixel 免费资产自动化入库(2025年版)

文章目录 一、背景二、问题讲解1. Quixel 免费资产是否还能一键入库?2. 是不是使用了一键入库功能 Quixel 的所有资产就能入库了?3. 一键入库会入库哪些资产?三、实现效果展示四、实现自动化入库五、常见问题1. 出现401报错2. 出现429报错3. 入库过于缓慢4. 入库 0 个资产一…