当前位置: 首页 > news >正文

C# 表达式树详解

总目录


前言

在 C# 中,表达式树(Expression Trees)是一种强大的特性,允许开发者将代码表示为数据结构。这使得我们可以在运行时动态地创建和操作代码逻辑,广泛应用于 LINQ 查询、动态方法生成以及反射等领域。本文将深入探讨 C# 表达式树的创建、操作和应用场景。


一、什么是表达式树?

1. 定义

表达式树是一种树状数据结构,将代码表示为一棵树,其中每个节点代表一个操作或数据(如属性访问、方法调用、运算等)。例如,表达式 x + y 可拆解为三个节点:参数 x、参数 y 和加法运算符。

在 C# 中,表达式树由 System.Linq.Expressions 命名空间中的类来表示。表达式树在C#中广泛应用于动态LINQ查询、动态编译等领域。

表达式树是一种数据结构,用于表示代码的逻辑结构(代码中的逻辑和运算)。与普通的委托不同,表达式树不是直接执行代码,而是将其转换为一个可查询和操作的对象模型。这使得我们可以动态地构建、分析和修改代码逻辑。

2. 表达式树的基本组件

  • Expression:表示表达式树的根节点或子表达式。
    • ParameterExpression:表示方法的参数。
    • MemberExpression:表示对对象成员(如属性或字段)的访问。
    • MethodCallExpression:表示对方法的调用。
    • BinaryExpression:表示二元运算(如加法、比较等)。
    • UnaryExpression:表示一元运算(如取反、递增等)。
    • ConstantExpression:表示常量值。
    • LambdaExpression:表示Lambda表达式主体。

2. 与委托和 Lambda 的区别

特性表达式树委托
表现形式可分析的树结构编译后的IL指令
运行时行为可动态修改和重组固定不可变
主要用途代码生成、动态查询直接方法调用
执行方式需编译为委托后执行直接调用

委托:编译时确定的函数指针,直接执行代码。
Lambda 表达式:可隐式转换为委托或表达式树。
表达式树:将代码逻辑存储为数据结构,支持运行时动态解析和修改。

3. 核心特性

不可变性:创建后无法修改,每次操作生成新树。
可编译性:通过 Compile() 方法生成委托执行。
高效性:相比反射,编译后的表达式树性能接近原生代码。

4. 表达式树的组成部分

一个典型的表达式树包含以下几个部分:

  • 参数:使用 ParameterExpression 定义lambda表达式的参数。
  • 主体:表达式树的主体可以是单一的表达式或多个表达式的组合,例如二元表达式 BinaryExpression 用于表示加法、乘法等操作。
  • lambda表达式:将参数和主体组合成一个lambda表达式,使用 Expression.Lambda 方法创建。

二、表达式树构建方式

1. 通过Lambda自动转换

Expression<Func<int, bool>> expr = num => num > 10 && num < 20;
public class Program
{public static void Main(){Expression<Func<int, bool>> expr = num => num > 10 && num < 20;Console.WriteLine(expr.ToString()); //输出:num => ((num > 10) AndAlso (num < 20))//编译并执行表达式树Func<int, bool> func = expr.Compile();        Console.WriteLine(func.Invoke(18)); //输出:True}
}

2. 手动构建表达式树

1)创建简单的表达式树

示例 1

表达式树通常使用 System.Linq.Expressions 命名空间中的类来构建。以下是一个简单的例子,展示如何创建一个基本的表达式树:

using System;
using System.Linq.Expressions;public class Program
{public static void Main(){// 创建参数表达式 xParameterExpression param = Expression.Parameter(typeof(int), "x");// 创建常量表达式 5ConstantExpression five = Expression.Constant(5, typeof(int));// 创建二元表达式 x + 5BinaryExpression add = Expression.Add(param, five);// 创建表达式树 x => (x + 5)Expression<Func<int, int>> expressionTree = Expression.Lambda<Func<int, int>>(add, param);Console.WriteLine(expressionTree);  // 输出: x => (x + 5)// 编译并执行表达式树Func<int, int> compiledFunc = expressionTree.Compile();Console.WriteLine(compiledFunc(10)); // 输出: 15}
}
示例 2

以下示例展示了如何创建一个简单的表达式树,该树表示两个数相加的表达式:

public class Program
{public static void Main(){// 创建参数表达式ParameterExpression a = Expression.Parameter(typeof(int), "a");ParameterExpression b = Expression.Parameter(typeof(int), "b");// 创建加法表达式BinaryExpression add = Expression.Add(a, b);// 创建 lambda 表达式Expression<Func<int, int, int>> lambda = Expression.Lambda<Func<int, int, int>>(add, a, b);Console.WriteLine(lambda);  // 输出:(a, b) => (a + b)// 编译并调用表达式树Func<int, int, int> func = lambda.Compile();int result = func(2, 3);Console.WriteLine($"Result: {result}"); // 输出: Result: 5}
}

2)构建复杂的表达式树

除了简单的算术运算,我们还可以构建更复杂的表达式树,例如条件判断、方法调用等。

示例1:基本组合
using System;
using System.Linq.Expressions;
public class Program
{public static void Main(){// 定义参数ParameterExpression x = Expression.Parameter(typeof(int), "x");// 创建表达式:x * 2BinaryExpression multiply = Expression.Multiply(x, Expression.Constant(2));// 创建表达式:x * 2 + 3BinaryExpression add = Expression.Add(multiply, Expression.Constant(3));// 创建lambda表达式Expression<Func<int, int>> lambda = Expression.Lambda<Func<int, int>>(add, x);// 输出lambda表达式Console.WriteLine(lambda); // 输出: x => (x * 2 + 3)// 编译lambda表达式Func<int, int> compiledLambda = lambda.Compile();// 执行编译后的lambda表达式int result = compiledLambda(5);Console.WriteLine($"Result of compiled lambda: {result}"); // 输出: 13}
}
示例2: 条件判断

下面的例子展示了如何构建一个包含条件判断的表达式树:

using System;
using System.Linq.Expressions;public class Program
{public static void Main(){// 创建参数表达式 xParameterExpression param = Expression.Parameter(typeof(int), "x");// 创建常量表达式 10ConstantExpression ten = Expression.Constant(10, typeof(int));// 创建条件表达式 x > 10BinaryExpression greaterThan = Expression.GreaterThan(param, ten);// 创建 if-true 表达式 x * 2BinaryExpression trueResult = Expression.Multiply(param, Expression.Constant(2, typeof(int)));// 创建 if-false 表达式 x - 1BinaryExpression falseResult = Expression.Subtract(param, Expression.Constant(1, typeof(int)));// 创建条件表达式ConditionalExpression conditional = Expression.Condition(greaterThan, trueResult, falseResult);// 创建表达式树 x=>(x > 10)? (x * 2):(x - 1)Expression<Func<int, int>> expressionTree = Expression.Lambda<Func<int, int>>(conditional, param);Console.WriteLine(expressionTree);  // 输出:x => IIF((x > 10), (x * 2), (x - 1))// 编译并执行表达式树Func<int, int> compiledFunc = expressionTree.Compile();Console.WriteLine(compiledFunc(15)); // 输出: 30Console.WriteLine(compiledFunc(5));  // 输出: 4}
}
示例3:方法调用

表达式树还可以用于表示方法调用。以下是一个调用静态方法的例子:

using System;
using System.Linq.Expressions;public class Program
{public static int Add(int a, int b){return a + b;}public static void Main(){// 创建参数表达式 x 和 yParameterExpression paramX = Expression.Parameter(typeof(int), "x");ParameterExpression paramY = Expression.Parameter(typeof(int), "y");// 获取 Add 方法的信息MethodInfo addMethod = typeof(Program).GetMethod("Add", new[] { typeof(int), typeof(int) });// 创建方法调用表达式MethodCallExpression call = Expression.Call(addMethod, paramX, paramY);// 创建表达式树Expression<Func<int, int, int>> expressionTree = Expression.Lambda<Func<int, int, int>>(call, paramX, paramY);// 编译并执行表达式树Func<int, int, int> compiledFunc = expressionTree.Compile();Console.WriteLine(compiledFunc(3, 7)); // 输出: 10}
}
示例4:复杂表达式树的动态拼接

表达式树支持动态逻辑,例如根据条件生成查询:

using System;
using System.Linq.Expressions;
public class Person
{public int Age { get; set; }public string Name { get; set; }
}
public class Program
{public static void Main(){// 动态生成 "p.Age > 18 && p.Name == "Alice""// 创建参数表达式 pvar param = Expression.Parameter(typeof(Person), "p");// 创建条件表达式 p.Age > 18var ageCheck = Expression.GreaterThan(Expression.Property(param, "Age"), Expression.Constant(18));// 创建条件表达式 p.Name == "Alice"var nameCheck = Expression.Equal(Expression.Property(param, "Name"), Expression.Constant("Alice"));// 创建条件表达式 p.Age > 18 && p.Name == "Alice"var combined = Expression.AndAlso(ageCheck, nameCheck);// 创建Lambda表达式 p=>p.Age > 18 && p.Name == "Alice"var lambda = Expression.Lambda<Func<Person, bool>>(combined, param);Console.WriteLine(lambda);  // 输出:p => ((p.Age > 18) AndAlso (p.Name == "Alice"))// 编译并执行表达式树Func<Person, bool> compiledFunc = lambda.Compile();Console.WriteLine(compiledFunc(new Person { Age = 16, Name = "Jack" }));  // 输出: FalseConsole.WriteLine(compiledFunc(new Person { Age = 19, Name = "Alice" })); // 输出: True}
}

此特性在 ORM 框架(如 Entity Framework)中用于动态生成 SQL。

三、应用场景

1. 应用场景

  • 动态查询:用于构建动态LINQ查询,特别适用于ORM框架。
  • 动态代码生成与编译:在运行时生成并编译代码,适用于需要动态生成逻辑的应用场景。
  • 元编程:通过解析和修改表达式树,实现高级编程模式,如方法拦截。
  • 动态条件判断:根据外部输入或配置动态调整业务逻辑。
  • 自定义运算符重载:扩展现有类型的运算符行为。

2. 应用场景示例

示例1:简单动态查询

表达式树最常见的一种应用是在动态查询中。例如,在LINQ to SQL中,表达式树被用来将C#查询转换为SQL查询。

using System;
using System.Linq;
using System.Linq.Expressions;public class Program
{public static void Main(){// 创建一个简单的列表var numbers = new[] { 1, 2, 3, 4, 5 };// 创建表达式树 x => x > 3ParameterExpression param = Expression.Parameter(typeof(int), "x");ConstantExpression three = Expression.Constant(3, typeof(int));BinaryExpression greaterThan = Expression.GreaterThan(param, three);Expression<Func<int, bool>> expressionTree = Expression.Lambda<Func<int, bool>>(greaterThan, param);// 使用表达式树进行查询var query = numbers.Where(expressionTree.Compile());Console.WriteLine(string.Join(",",query)); // 输出:4,5}
}

示例2:动态构建复杂查询条件

在 LINQ 查询中,表达式树可以用于动态构建查询条件,使得查询更加灵活。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;public class Product
{public int Id { get; set; }public string Name { get; set; }public decimal Price { get; set; }
}public class Program
{public static void Main(){List<Product> products = new List<Product>{new Product { Id = 1, Name = "Laptop", Price = 999.99m },new Product { Id = 2, Name = "Mouse", Price = 19.99m },new Product { Id = 3, Name = "Keyboard", Price = 49.99m }};// 动态构建查询条件 p.Price >= 50.00ParameterExpression parameter = Expression.Parameter(typeof(Product), "p");MemberExpression priceMember = Expression.Property(parameter, "Price");ConstantExpression minPrice = Expression.Constant(50.00m);BinaryExpression condition = Expression.GreaterThanOrEqual(priceMember, minPrice);// 构建 lambda 表达式 p => p.Price >= 50.00Expression<Func<Product, bool>> predicate = Expression.Lambda<Func<Product, bool>>(condition, parameter);// 使用表达式树进行查询var expensiveProducts = products.Where(predicate.Compile());Console.WriteLine("Products with price >= 50.00:");foreach (var product in expensiveProducts){Console.WriteLine($"{product.Name}: {product.Price}");}}
}

运行结果:

Products with price >= 50.00:
Laptop: 999.99

示例3:动态生成代码

表达式树还可以用于在运行时生成并编译新的代码。这对于需要根据用户输入或配置动态生成逻辑的应用程序非常有用。

using System;
using System.Linq.Expressions;public class Program
{public static void Main(){// 动态生成一个 lambda 表达式 x => x * 2ParameterExpression param = Expression.Parameter(typeof(int), "x");BinaryExpression multiply = Expression.Multiply(param, Expression.Constant(2, typeof(int)));Expression<Func<int, int>> expressionTree = Expression.Lambda<Func<int, int>>(multiply, param);// 编译并执行表达式树Func<int, int> compiledFunc = expressionTree.Compile();Console.WriteLine(compiledFunc(5)); // 输出: 10}
}

示例4:元编程

表达式树可以用于元编程,即编写能够生成或修改其他代码的代码。例如,可以通过解析表达式树来实现AOP(面向切面编程)中的方法拦截功能。

假设我们有一个简单的日志拦截器,可以在方法调用前后记录日志。

using System;
using System.Linq.Expressions;
using System.Reflection;public class Program
{public static void Main(){// 创建表达式树来调用 Add 方法ParameterExpression paramX = Expression.Parameter(typeof(int), "x");ParameterExpression paramY = Expression.Parameter(typeof(int), "y");MethodInfo addMethod = typeof(Program).GetMethod("Add", BindingFlags.Public | BindingFlags.Static);MethodCallExpression call = Expression.Call(addMethod, paramX, paramY);// 创建带日志的表达式树Expression<Action<int, int>> logExpression = CreateLoggedExpression<int, int>(call, paramX, paramY);// 编译并执行表达式树Action<int, int> loggedAction = logExpression.Compile();loggedAction(3, 7);}public static int Add(int a, int b){Console.WriteLine($"Add:{a} + {b} ={a + b}");return a + b;}private static Expression<Action<T1, T2>> CreateLoggedExpression<T1, T2>(MethodCallExpression methodCall, ParameterExpression param1, ParameterExpression param2){//创建 代码块var block = Expression.Block(Expression.Call(null, typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }), Expression.Constant("Entering Add")),methodCall,Expression.Call(null, typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }), Expression.Constant("Exiting Add")));//以上功能类似于//Console.WriteLine("Entering Add");//Add(10,10);//Console.WriteLine("Exiting Add");return Expression.Lambda<Action<T1, T2>>(block, param1, param2);}
}

运行结果:

Entering Add
Add:3 + 7 =10
Exiting Add

示例5:动态条件判断

表达式树可以用于动态构建条件判断逻辑,适用于需要根据外部输入或配置动态调整业务逻辑的场景。

using System;
using System.Linq.Expressions;public class Program
{public static void Main(){// 创建参数表达式 xParameterExpression param = Expression.Parameter(typeof(int), "x");// 创建常量表达式 10ConstantExpression ten = Expression.Constant(10, typeof(int));// 创建条件表达式 x > 10BinaryExpression greaterThan = Expression.GreaterThan(param, ten);// 创建 if-true 表达式 x * 2BinaryExpression trueResult = Expression.Multiply(param, Expression.Constant(2, typeof(int)));// 创建 if-false 表达式 x - 1BinaryExpression falseResult = Expression.Subtract(param, Expression.Constant(1, typeof(int)));// 创建条件表达式ConditionalExpression conditional = Expression.Condition(greaterThan, trueResult, falseResult);// 创建表达式树Expression<Func<int, int>> expressionTree = Expression.Lambda<Func<int, int>>(conditional, param);// 编译并执行表达式树Func<int, int> compiledFunc = expressionTree.Compile();Console.WriteLine(compiledFunc(15)); // 输出: 30Console.WriteLine(compiledFunc(5));  // 输出: 4}
}

示例6:自定义运算符重载

表达式树可以用于实现自定义运算符重载,适用于需要扩展现有类型的行为的场景。

假设我们有一个自定义类型 Vector,并希望为其添加加法运算符。

using System;
using System.Linq.Expressions;public struct Vector
{public double X { get; set; }public double Y { get; set; }public Vector(double x, double y){X = x;Y = y;}public static Vector operator +(Vector v1, Vector v2){return new Vector(v1.X + v2.X, v1.Y + v2.Y);}
}public class Program
{public static void Main(){// 创建两个 Vector 实例Vector v1 = new Vector(1, 2);Vector v2 = new Vector(3, 4);// 创建表达式树来表示 v1 + v2ParameterExpression paramV1 = Expression.Parameter(typeof(Vector), "v1");ParameterExpression paramV2 = Expression.Parameter(typeof(Vector), "v2");BinaryExpression add = Expression.Add(paramV1, paramV2);// 创建表达式树Expression<Func<Vector, Vector, Vector>> expressionTree = Expression.Lambda<Func<Vector, Vector, Vector>>(add, paramV1, paramV2);// 编译并执行表达式树Func<Vector, Vector, Vector> compiledFunc = expressionTree.Compile();Vector result = compiledFunc(v1, v2);Console.WriteLine($"({result.X}, {result.Y})"); // 输出: (4, 6)}
}

3. 高级实战场景

场景1:动态查询构造

using System;
using System.Linq.Expressions;public class Person
{public int Age { get; set; }public string Name { get; set; }
}public class Program
{public static IQueryable<Person> BuildDynamicQuery(IQueryable<Person> source, string propertyName, object value){var param = Expression.Parameter(typeof(Person), "p");var property = Expression.Property(param, propertyName);var constant = Expression.Constant(value);var body = Expression.Equal(property, constant);var lambda = Expression.Lambda<Func<Person, bool>>(body, param);return source.Where(lambda);}public static void Main(){List<Person> people = new List<Person>{new Person { Name = "Alice", Age = 30 },new Person { Name = "Bob", Age = 25 },new Person { Name = "Charlie", Age = 35 },new Person { Name = "David", Age = 25 }};// 通过 BuildDynamicQuery 方法 可以动态筛选 特定属性等于特定值的数据var query = BuildDynamicQuery(people.AsQueryable(), "Age", 25);Console.WriteLine(string.Join(",",query.Select(x=>x.Name)));// 输出:Bob,David}
}

如果是在ORM中,则会通过如下形式调用:

// 使用示例
var query = BuildDynamicQuery(dbContext.Persons, "Age", 25);

场景2:高性能对象映射

using System;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;public static class Mapper<TSource, TTarget>
{private static readonly ConcurrentDictionary<(Type, Type), Func<TSource, TTarget>> _cache = new ConcurrentDictionary<(Type, Type), Func<TSource, TTarget>>();public static Func<TSource, TTarget> CreateMapper(){var cacheKey = (typeof(TSource), typeof(TTarget));return _cache.GetOrAdd(cacheKey, key =>{var sourceParam = Expression.Parameter(typeof(TSource), "source");var bindings = new List<MemberBinding>();foreach (var targetProp in typeof(TTarget).GetProperties(BindingFlags.Public | BindingFlags.Instance)){var sourceProp = typeof(TSource).GetProperty(targetProp.Name, BindingFlags.Public | BindingFlags.Instance);if (sourceProp == null || !sourceProp.CanRead || !targetProp.CanWrite)continue;// 检查属性类型是否匹配if (sourceProp.PropertyType != targetProp.PropertyType)continue;var propAccess = Expression.Property(sourceParam, sourceProp);bindings.Add(Expression.Bind(targetProp, propAccess));}var body = Expression.MemberInit(Expression.New(typeof(TTarget)),bindings);var lambda = Expression.Lambda<Func<TSource, TTarget>>(body, sourceParam);return lambda.Compile();});}
}// 示例类定义
public class Person
{public int Id { get; set; }public string Name { get; set; }public DateTime BirthDate { get; set; }
}public class PersonDto
{public int Id { get; set; }public string Name { get; set; }public DateTime BirthDate { get; set; }
}public class Program
{public static void Main(string[] args){var person = new Person{Id = 1,Name = "John Doe",BirthDate = new DateTime(1990, 1, 1)};var mapper = Mapper<Person, PersonDto>.CreateMapper();var dto = mapper(person);Console.WriteLine($"Id: {dto.Id}, Name: {dto.Name}, BirthDate: {dto.BirthDate}");}
}

四、高级技巧与优化

1. 表达式树缓存策略

private static ConcurrentDictionary<string, Delegate> _cache = new ConcurrentDictionary<string, Delegate>();public Func<TInput, TResult> GetOrCreateCompiler<TInput, TResult>(Expression<Func<TInput, TResult>> expr)
{string key = expr.ToString();return (Func<TInput, TResult>)_cache.GetOrAdd(key, _ => {return expr.Compile();});
}

2. 动态修改表达式树

public class Program
{public static void Main(string[] args){Expression<Func<int, int>> original = x => x * 2;var modifier = new ExpressionModifier();var modifiedExpr = (Expression<Func<int, int>>)modifier.Visit(original);Console.WriteLine(modifiedExpr);    //输出:x => (x + 2)var func = modifiedExpr.Compile();Console.WriteLine(func(1)); // 输出:3}
}class ExpressionModifier : ExpressionVisitor
{protected override Expression VisitBinary(BinaryExpression node){if (node.NodeType == ExpressionType.Multiply){return Expression.Add(node.Left, node.Right);}return base.VisitBinary(node);}
}

五、性能关键点

1. 编译性能对比

操作耗时(μs)
直接调用0.01
委托调用0.03
首次编译表达式树100-500
缓存后表达式树调用0.05

2. 优化建议

  • 预编译高频表达式:启动时编译常用表达式
  • 避免重复编译:使用字典缓存编译结果,避免重复编译高频使用的表达式树。
  • 减少闭包捕获:优先使用静态方法
  • 使用Expression.Compile(preferInterpretation: true):.NET Core 3.0+支持解释模式
  • 避免过度动态化:静态代码在可预测场景下更高效。

六、使用须知

1. 注意事项

1)不可变性

修改需重新生成树结构

2)平台限制

  • 平台兼容性:确保目标环境支持 System.Linq.Expressions
  • AOT编译环境(如iOS):部分动态编译功能受限
  • 部分表达式节点:在Entity Framework等Provider中不支持

3)安全风险

// 危险:允许用户输入表达式
var expr = "x => x.Name.Contains(\"" + userInput + "\")";
// 可能引发注入攻击,应使用白名单验证

4)调试复杂性

动态生成的树结构难以直接调试,需借助可视化工具。

2. 表达式树的优缺点

1)优点

  • 动态性:允许在运行时动态生成和修改代码逻辑。
  • 灵活性:适用于延迟执行和针对不同数据源的优化查询。
  • 可分析性:表达式树可以被分析和转换,便于实现复杂的逻辑。
  • 性能优化:通过编译表达式树生成的代码,可以获得接近直接编译代码的性能。
  • 代码复用:表达式树可以复用现有的逻辑,减少重复代码的编写。

2)缺点

  • 性能开销:表达式树的构建和编译需要额外的性能开销。
  • 复杂性:表达式树的代码相对复杂,学习曲线较陡。

结语

回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


参考资料:
MSDN文档 表达式树
DLR (Dynamic Language Runtime)
c#:表达式树概念及应用场景(Expression)
c#:深入理解表达式树

相关文章:

C# 表达式树详解

总目录 前言 在 C# 中&#xff0c;表达式树&#xff08;Expression Trees&#xff09;是一种强大的特性&#xff0c;允许开发者将代码表示为数据结构。这使得我们可以在运行时动态地创建和操作代码逻辑&#xff0c;广泛应用于 LINQ 查询、动态方法生成以及反射等领域。本文将深…...

分别用树型和UML结构展示java集合框架常见接口和类

树型结构展示java集合框架常见接口和类 Java 集合框架中的接口和子类关系可以用树形结构来展示。以下是一个简化的树形结构&#xff0c;展示了主要的接口和一些重要的实现类&#xff1a; java.util.Collection ├── java.util.List │ ├── java.util.ArrayList │ ├…...

蓝桥杯备赛-二分-青蛙过河

问题描述 小青蛙住在一条河边, 它想到河对岸的学校去学习。小青蛙打算经过河里 的石头跳到对岸。 河里的石头排成了一条直线, 小青蛙每次跳跃必须落在一块石头或者岸上。 不过, 每块石头有一个高度, 每次小青蛙从一块石头起跳, 这块石头的高度就 会下降 1 , 当石头的高度下降…...

uniapp+微信小程序+最简单局部下拉刷新实现

直接上代码 <scroll-view style"height: 27vh;" :scroll-top"scrollTop" scroll-y"true"scrolltolower"onScrollToLower1" lower-threshold"50"refresher-enabled"true" refresherrefresh"onRefresherR…...

Spring Boot 3.x 中 @NotNull 与 @NonNull 的深度解析

在 Java 开发领域&#xff0c;尤其是在 Spring Boot 生态系统中&#xff0c;空指针异常&#xff08;NPEs&#xff09;始终是一个顽固的挑战。这些运行时错误可能导致应用程序崩溃、数据不一致以及糟糕的用户体验。为了应对这一问题&#xff0c;Java 社区开发了各种空安全机制&a…...

SQLark 实战 | 如何从Excel、csv、txt等外部文件进行数据导入

数据导入导出是应用开发者在平时开发中最常用的操作之一&#xff0c;SQLark 里提供了方便的图形化界面来完成导入导出。本文先和大家分享如何从 Excel、csv、txt 等外部文件导入数据到数据库表中。 &#x1f449; 前往 SQLark 官网&#xff1a;www.sqlark.com 下载全功能免费版…...

MATLAB中envelope函数使用

目录 说明 示例 chirp 的解析包络 使用滤波器计算多通道信号的解析包络 录音信号的移动 RMS 包络 语音信号的峰值包络 不对称序列的包络 envelope函数的功能是提取信号的包络。 语法 [yupper,ylower] envelope(x) [yupper,ylower] envelope(x,fl,analytic) [yupper,…...

ES搭建详细指南+常见错误解决方法

Elasticsearch&#xff08;ES&#xff09;是一款开源的、分布式的、RESTful风格的搜索和数据分析引擎。它用于全文搜索、结构化搜索、分析等场景。以下是Elasticsearch的搭建步骤以及处理常见错误的方法。 Elasticsearch搭建步骤&#xff1a; 1.环境准备&#xff1a; 确保你的…...

Unity 封装一个依赖于MonoBehaviour的计时器(上) 基本功能

灵感来自下面这本书的协程部分,因此我就自己尝试写了一个 我的新书Unity3D游戏开发&#xff08;第3版&#xff09; | 雨松MOMO程序研究院 如果你不知道什么是协程:unity保姆级教程之协同程序_unity协同-CSDN博客 一句话概括:协程就是单线程的异步操作,其作用于Unity的主线程 1…...

PostgreSQL数据库版本支持策略

PostgreSQL数据库版本支持策略 主要版本会进行复杂的更改&#xff0c;因此无法以向后兼容的方式维护数据目录的内容。重大升级需要转储/重新加载数据库或使用pg_upgrade应用程序。我们还建议您阅读您计划升级到的主要版本的升级部分。您可以从一个主要版本升级到另一个&#xf…...

应用层之网络应用模型,HTTP/HTTPS协议

应用层是网络协议栈的最顶层&#xff0c;直接为应用程序提供通信服务&#xff0c;定义了不同主机间应用进程交互的规则&#xff0c;包括报文类型、语法、语义及通信时序 一、网络应用模型 1.定义及特点 模型定义核心特点典型应用场景C/S客户端向服务器发起请求&#xff0c;服…...

(七)Spring Boot学习——Redis使用

有部分内容是常用的&#xff0c;为了避免每次都查询数据库&#xff0c;将部分数据存入Redis。 一、 下载并安装 Redis Windows 版的 Redis 官方已不再维护&#xff0c;你可以使用 微软提供的 Redis for Windows 版本 或者 使用 WSL&#xff08;Windows Subsystem for Linux&a…...

11 | 给 Gin 服务器添加中间件

提示&#xff1a; 所有体系课见专栏&#xff1a;Go 项目开发极速入门实战课&#xff1b;欢迎加入 云原生 AI 实战 星球&#xff0c;12 高质量体系课、20 高质量实战项目助你在 AI 时代建立技术竞争力&#xff08;聚焦于 Go、云原生、AI Infra&#xff09;&#xff1b;本节课最终…...

selenium等待

通常代码执行的速度⽐页⾯渲染的速度要快,如果避免因为渲染过慢出现的⾃动化误报的问题呢?可以使⽤selenium中提供的三种等待⽅法: 1. 隐式等待(Implicit Wait) 隐式等待适用于全局,它告诉 WebDriver 在查找元素时等待一定的时间,直到元素出现。 如果超时,WebDriver 不…...

为什么List、Set集合无法在遍历的时候修改内部元素

以常用集合ArrayList为例&#xff0c;ArrayList 在遍历过程中无法直接修改内部元素的结构&#xff08;例如通过 remove() 或 add() 方法修改元素&#xff09;&#xff0c;是因为 遍历的过程中修改结构 可能会导致 不一致的行为、并发修改异常 或 逻辑错误。 注意&#xff1a;和…...

使用 Elasticsearch 构建多模式 RAG 系统:哥谭市的故事

作者&#xff1a;来自 Elastic Alex Salgado 学习如何构建一个多模态检索增强生成 (RAG) 系统&#xff0c;该系统集成文本、音频、视频和图像数据&#xff0c;以提供更丰富的、具有上下文的信息检索。 在这篇博客中&#xff0c;你将学习如何使用 Elasticsearch 构建一个多模态 …...

单一责任原则在Java设计模式中的深度解析

在软件开发中&#xff0c;设计模式提供了一种解决特定问题的思路。在众多的设计原则中&#xff0c;单一责任原则&#xff08;Single Responsibility Principle&#xff0c;SRP&#xff09;是一个非常重要的概念。它主要强调一个类应该只有一个责任&#xff0c;也就是说&#xf…...

设计模式学习记录

设计模式23种 创建型抽象工厂模式工厂模式生成器模式原型模式单例模式 结构型适配器模式桥接模式组合模式装饰模式外观模式享元模式代理模式 行为型责任链模式命令模式解释器模式迭代器模式中介者模式备忘录模式观察者模式状态模式策略模式模版方法模式访问者模式 创建型 与对…...

set_clock_groups

一、命令参数与工具处理逻辑 核心参数定义 参数定义工具行为工具兼容性-asynchronous完全异步时钟组&#xff0c;无任何相位或频率关系&#xff08;如独立晶振、不同时钟树&#xff09;工具完全禁用组间路径的时序分析&#xff0c;但需用户自行处理跨时钟域&#xff08;CDC&a…...

QT创建项目(项目模板、构建系统、选择类、构建套件)

1. 项目模版 项目类型界面技术适用场景核心依赖模块开发语言Qt Widget ApplicationC Widgets传统桌面应用&#xff08;复杂控件&#xff09;Qt WidgetsCQt Console Application无 GUI命令行工具、服务Qt CoreCQt Quick ApplicationQML/Quick现代跨平台应用&#xff08;动画/触…...

麒麟系统利用pycharm生成deb文件

在麒麟系统&#xff08;Kylin OS&#xff09;上使用 PyCharm 进行 Python 开发并生成 .deb 可安装软件包&#xff0c;可以按照以下步骤进行操作&#xff1a; 1. 准备工作 安装 PyCharm&#xff1a;确保已经在麒麟系统上安装了 PyCharm&#xff0c;可以使用官方提供的安装包进…...

超声重建,3D重建 超声三维重建,三维可视化平台 UR 3D Reconstruction

1. 超声波3D重建技术的实现方法与算法 技术概述 3D超声重建是一种基于2D超声图像生成3D体积数据的技术&#xff0c;广泛应用于医学影像领域。通过重建和可视化三维结构&#xff0c;3D超声能够显著提高诊断精度和效率&#xff0c;同时减少医生的脑力负担。本技术文档将详细阐述…...

Qt 信号与槽

目录 Qt信号和槽 connect函数 connect使用方法 自定义信号 与 自定义槽 Qt界面化工具自动生成的槽 自定义信号 带参数的信号和槽 信号与槽的断开 Qt信号和槽 谈到信号&#xff0c;设计3个要素 信号源&#xff1a;谁发出了信号 信号触发条件&#xff1a;哪个控件的哪个…...

卷积神经网络 - 卷积的变种、数学性质

本文我们来学习卷积的变种和相关的数学性质&#xff0c;为后面学习卷积神经网络做准备&#xff0c;有些概念可能不好理解&#xff0c;可以先了解其概念&#xff0c;然后慢慢理解、逐步深入。 在卷积的标准定义基础上&#xff0c;还可以引入卷积核的滑动步长和零填充来增加卷积…...

ubuntu 和 RV1126 交叉编译Mosqutiio-1.6.9

最近需要交叉编译mosquitto&#xff0c;遇到一些小问题记录一下。 1.众所周知使用它自带的Makefile编译的时候&#xff0c;只需要在编译前&#xff0c;指定它config.mk中的变量&#xff1a;CFLAGS头文件路径 和 LDFLAGS库文件路径就ok&#xff0c;例子如下&#xff1a; expor…...

从零开始学习机器人---如何高效学习机械原理

如何高效学习机械原理 1. 理解课程的核心概念2. 结合图形和模型学习3. 掌握公式和计算方法4. 理论与实践相结合5. 总结和复习6. 保持好奇心和探索精神 总结 机械原理是一门理论性和实践性都很强的课程&#xff0c;涉及到机械系统的运动、动力传递、机构设计等内容。快速学习机械…...

STM32 RS232通信开发全解析 | 零基础入门STM32第五十九步

主题内容教学目的/扩展视频RS232串口电路原理&#xff0c;跳线设置&#xff0c;驱动程序。与超级终端通信。了解电路原理和RS232协议。 师从洋桃电子&#xff0c;杜洋老师 &#x1f4d1;文章目录 一、RS232通信系统架构二、RS232核心原理与硬件设计2.1 电气特性对比2.2 典型电路…...

文献分享: 对ColBERT段落多向量的剪枝——基于学习的方法

原论文 1. 导论 & \textbf{\&} &方法 1️⃣要干啥&#xff1a;在 ColBERT \text{ColBERT} ColBERT方法中&#xff0c;限制每个段落要保留的 Token \text{Token} Token的数量&#xff0c;或者说对段落 Token \text{Token} Token进行剪枝 2️⃣怎么干&#xff1a;注…...

(已解决)aws 上 部署Splunk 负载均衡unhealthy

在AWS 部署Splunk 服务,instance 是后端的EC2, 我把splunk 服务起好后,发现port : 8000 是listening: #netstat -an | grep 80 tcp 0 0 127.0.0.1:8065 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:8089 0.0.0.0:* …...

C# 异步编程

概述 同步&#xff1a;指必须等待前一个操作完成&#xff0c;后续操作才能继续。同步操作会阻塞线程直到任务完成。 异步&#xff1a;异步操作不会阻塞线程&#xff0c;允许程序在等待某个任务完成的同时&#xff0c;继续执行其他任务。 异步编程适用场景&#xff1a; 1、从…...

缓存之美:Guava Cache 相比于 Caffeine 差在哪里?

大家好&#xff0c;我是 方圆。本文将结合 Guava Cache 的源码来分析它的实现原理&#xff0c;并阐述它相比于 Caffeine Cache 在性能上的劣势。为了让大家对 Guava Cache 理解起来更容易&#xff0c;我们还是在开篇介绍它的原理&#xff1a; Guava Cache 通过分段&#xff08;…...

Go string 字符串底层逻辑

在 Go 语言中&#xff0c;string 类型的底层结构是一个结构体&#xff0c;包含两个字段&#xff1a;一个指向字节数组的指针和该字节数组的长度。以下是其在 Go 源码中的大致定义&#xff1a;type stringStruct struct {str unsafe.Pointerlen int } str&#xff1a;这是一个指…...

高效集成聚水潭采购退货数据到MySQL的最佳实践

聚水潭数据集成到MySQL&#xff1a;采购退货单的高效对接方案 在企业的数据管理和分析过程中&#xff0c;数据的准确性和实时性至关重要。本文将分享一个具体的系统对接集成案例&#xff1a;如何通过轻易云数据集成平台&#xff0c;将聚水潭中的采购退货单数据高效地集成到MyS…...

STM32步进电机S型与T型加减速算法

目录 一、基本原理 二、常见类型 三、算法详解 四、应用场合 五、代码实现 1、main...

centos操作系统上传和下载百度网盘内容

探序基因 整理 进入百度网盘官网百度网盘 客户端下载 下载linux的rpm格式的安装包 在linux命令行中输入&#xff1a;rpm -ivh baidunetdisk_4.17.7_x86_64.rpm 出现报错&#xff1a; 错误&#xff1a;依赖检测失败&#xff1a; libXScrnSaver 被 baidunetdisk-4.17.7-1.x8…...

深入 Python 网络爬虫开发:从入门到实战

一、为什么需要爬虫&#xff1f; 在数据驱动的时代&#xff0c;网络爬虫是获取公开数据的重要工具。它可以帮助我们&#xff1a; 监控电商价格变化抓取学术文献构建数据分析样本自动化信息收集 二、基础环境搭建 1. 核心库安装 pip install requests beautifulsoup4 lxml …...

网络爬虫【简介】

我叫补三补四&#xff0c;很高兴见到大家&#xff0c;欢迎一起学习交流和进步 今天来讲一讲爬虫 一、网络爬虫的定义 网络爬虫&#xff08;Web Crawler&#xff09;&#xff0c;又称为网络蜘蛛、网络机器人等&#xff0c;是一种按照一定规则自动抓取互联网信息的程序或脚本。它…...

Linux:Ubuntu server 24.02 上搭建 ollama + dify

一、安装Ubuntu 具体的安装过程可以参见此链接&#xff1a;链接&#xff1a;Ubuntu Server 20.04详细安装教程&#xff0c;这里主要记录一下过程中遇到的问题。 安装时subnet如何填写 在Ubuntu中subnet填写255.255.255.0是错误的&#xff0c;其格式为 xx.xx.xx.xx/yy &#…...

【生日蛋糕——DFS剪枝优化】

题目 分析 代码 #include <bits/stdc.h> using namespace std;const int N 24; const int inf 0x3f3f3f3f;int mins[N], minv[N]; int R[N], H[N]; int n, m, ans inf;void dfs(int u, int v, int s) {if(v minv[u] > n) return;if(s mins[u] > ans) return;…...

RabbitMq C++客户端的使用

1.RabbitMq介绍 RabbitMQ 是一款开源的消息队列中间件&#xff0c;基于 AMQP&#xff08;高级消息队列协议&#xff09;实现&#xff0c;支持多种编程语言和平台。以下是其核心特点和介绍&#xff1a; 核心特点 多语言支持 提供 Java、Python、C#、Go、JavaScript 等语言的客…...

入门基础项目-前端Vue_02

文章目录 1. 用户信息1.1 整体设计1.2 完整代码 User.vue1.2.1 数据加载1.2.2 表格 el-table1.2.2.1 多选1.2.2.2 自定义列的内容 Slot1.2.2.3 图片 el-image1.2.2.4 分页 el-pagination 1.2.3 编辑1.2.3.1 弹出框 el-dialog1.2.3.2 上传 el-upload 1.2.4 新增1.2.5 删除1.2.6 …...

C#中SerialPort 的使用

最近在学习C#的SerialPort &#xff0c;关于SerialPort 的使用&#xff0c;做如下总结&#xff1a; 1.可以通过函数System.IO.Ports.SerialPort.GetPortNames() 将获得系统所有的串口名称。C#代码如下&#xff1a; string[] sPorts SerialPort.GetPortNames(); foreach(stri…...

使用py-ffmpeg批量合成视频的脚本

我有一个小米摄像头&#xff0c;用它录出来的视频全部都是3s一段3s一段的。其中有几个小时的视频我需要保存&#xff0c;当初直接把摄像头的卡文件导出来重命名掉了&#xff0c;那时候没有注意&#xff0c;之后想剪辑/发送给别人的时候发现疯了&#xff1a; 1.剪辑的话&#x…...

mac安装navicat及使用

0.删除旧的 sudo rm -Rf /Applications/Navicat\ Premium.app sudo rm -Rf /private/var/db/BootCaches/CB6F12B3-2C14-461E-B5A7-A8621B7FF130/app.com.prect.NavicatPremium.playlist sudo rm -Rf ~/Library/Caches/com.apple.helpd/SDMHelpData/Other/English/HelpSDMIndexF…...

织梦dedecmsV5.7提示信息提示框美化(带安装教程和效果展示)

一、效果展示 1、安装前效果 2、安装后效果 二、安装说明 1、安装测试版本&#xff1a;DedeCMS-V5.7.117-UTF8&#xff1b; 2、必须在修改代码之前请做好文件备份&#xff0c;以免误操无法恢复&#xff1b; 3、为了兼容其他版本&#xff0c;请在安装时&#xff0c;最好将替…...

【知识迁移的底层逻辑:从符号到语义的升维】

大语言模型&#xff08;LLMs&#xff09;能够通过有限语料库实现广泛知识迁移并回答多样化问题&#xff0c;其核心机制在于抽象模式学习、上下文推理能力及知识组合泛化&#xff0c;而非简单的数据记忆。以下是具体实现路径与技术原理&#xff1a; 一、知识迁移的底层逻辑&…...

Windows根据文件名批量在文件夹里查找文件并复制出来,用WPF实现的详细步骤

项目前言 在日常工作和生活中&#xff0c;我们常常会遇到需要从大量文件中根据文件名批量查找特定文件并复制到指定位置的情况。手动一个个查找和复制文件不仅效率低下&#xff0c;还容易出错。使用 Windows Presentation Foundation (WPF) 可以创建一个用户友好的图形界面应用…...

Certbot实现SSL免费证书自动续签(CentOS 7版 + Docker部署的nginx)

前置安装&#xff0c;可参考Certbot实现SSL免费证书自动续签&#xff08;CentOS 7 nginx/apache&#xff09; 如果是通过 Docker 运行 Nginx&#xff0c; certbot 无法直接检测到本地的 Nginx 配置。解决方案是 使用 standalone 模式 或 挂载 Webroot 方式获取 SSL 证书&…...

一周学会Flask3 Python Web开发-SQLAlchemy查询所有数据操作-班级模块

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们来新建一个的蓝图模块-班级模块&#xff0c;后面可以和学生模块&#xff0c;实现一对多的数据库操作。 blueprint下新建g…...

工程实践:如何使用SU17无人机来实现室内巡检任务

阿木实验室最近发布了科研开发者版本的无人机SU17&#xff0c;该无人机上集成了四目视觉&#xff0c;三维激光雷达&#xff0c;云台吊舱&#xff0c;高算力的机载计算机&#xff0c;是一个非常合适的平台用于室内外巡检场景。同时阿木实验室维护了多个和无人机相关的开源项目。…...