欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 时评 > C#扩展方法与Lambda表达式基本用法

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

2025/5/2 1:04:01 来源:https://blog.csdn.net/XiaoWang_csdn/article/details/147641489  浏览:    关键词: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#代码的表达力和简洁性,同时保持良好的可维护性和性能。在实际开发中,应根据具体场景选择最合适的模式和技术。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词