06-C#

C# Modbus通信从入门到精通(27)——Modbus TCP(0x06功能码与C#代码实现) 本文详细介绍了ModbusTCP协议中0x06写单个寄存器功能码的原理、发送报文、返回报文,由高级工程师多年经验总结而成,绝对干货! 阅读详情

C#.Net-表达式目录树-学习笔记

一、本质与核心概念

表达式目录树是一种数据结构,以树形结构描述代码逻辑。它不是可执行代码,而是对代码结构的描述。

  • 委托(Delegate):编译后直接生成 IL,可以直接执行
  • 表达式目录树(Expression Tree):是一个树形数据对象,描述了计算关系,不能直接执行
  • Expression<Func<T>> vs Func<T>:前者是表达式树,后者是委托,二者可通过 .Compile() 单向转换
  • 语法限制:表达式目录树只能是单行 Lambda,不能有大括号和多行语句
  • 树形结构:类似二叉树,每个节点都是一个子表达式,可以一层层拆解或拼装
// 委托:直接可执行
Func<int, int, int> func = (m, n) => m * n + 2;

// 表达式目录树:描述结构,需要 Compile() 后才能执行
Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;
Func<int, int, int> compiled = exp.Compile();
int result = compiled(10, 20); // 202

表达式 (m, n) => m * n + 2 的树形结构:

+(加法)
       / \
      *   2(常量)
     / \
    m   n(参数)

二、动态拼装表达式目录树

拼装的核心思路:把最小粒度的节点逐步组合成完整表达式,最后包裹成 Lambda。

2.1 基础:无参数

// 目标:() => 123 + 234
ConstantExpression c1 = Expression.Constant(123);
ConstantExpression c2 = Expression.Constant(234);
BinaryExpression add = Expression.Add(c1, c2);
Expression<Func<int>> lambda = Expression.Lambda<Func<int>>(add);
int result = lambda.Compile()(); // 357

2.2 带参数

// 目标:m => m + 1
ParameterExpression paramM = Expression.Parameter(typeof(int), "m");
ConstantExpression c1 = Expression.Constant(1);
BinaryExpression add = Expression.Add(paramM, c1);
Expression<Func<int, int>> lambda = Expression.Lambda<Func<int, int>>(add, paramM);
int result = lambda.Compile()(5); // 6

2.3 访问属性/字段

// 目标:c => c.Id == 10
ParameterExpression paramC = Expression.Parameter(typeof(People), "c");

// 字段用 Expression.Field,属性用 Expression.Property
FieldInfo fieldId = typeof(People).GetField("Id");
MemberExpression idExp = Expression.Field(paramC, fieldId);

ConstantExpression c10 = Expression.Constant(10);
BinaryExpression equal = Expression.Equal(idExp, c10);

Expression<Func<People, bool>> lambda =
    Expression.Lambda<Func<People, bool>>(equal, paramC);

2.4 调用方法

// 目标:c => c.Name.Equals("Richard")
ParameterExpression paramC = Expression.Parameter(typeof(People), "c");
PropertyInfo propName = typeof(People).GetProperty("Name");
MemberExpression nameExp = Expression.Property(paramC, propName);

MethodInfo equalsMethod = typeof(string).GetMethod("Equals", new[] { typeof(string) });
ConstantExpression constRichard = Expression.Constant("Richard");

MethodCallExpression callExp = Expression.Call(nameExp, equalsMethod, constRichard);

// 包装成 Lambda 并执行
Expression<Func<People, bool>> lambda =
    Expression.Lambda<Func<People, bool>>(callExp, paramC);
bool result = lambda.Compile()(new People { Name = "Richard" }); // true

2.5 复杂表达式:建议从右往左拼装

// 目标:c => c.Id.ToString() == "10" && c.Name.Equals("Richard") && c.Age > 35

ParameterExpression paramC = Expression.Parameter(typeof(People), "c");

// 1. c.Age > 35
var ageExp = Expression.Property(paramC, typeof(People).GetProperty("Age"));
var ageGreater = Expression.GreaterThan(ageExp, Expression.Constant(35));

// 2. c.Name.Equals("Richard")
var nameExp = Expression.Property(paramC, typeof(People).GetProperty("Name"));
var equalsMethod = typeof(string).GetMethod("Equals", new[] { typeof(string) });
var nameEquals = Expression.Call(nameExp, equalsMethod, Expression.Constant("Richard"));

// 3. c.Id.ToString() == "10"
var idExp = Expression.Field(paramC, typeof(People).GetField("Id"));
var toStringMethod = typeof(int).GetMethod("ToString", Type.EmptyTypes);
var toStringExp = Expression.Call(idExp, toStringMethod);
var idEqual = Expression.Equal(toStringExp, Expression.Constant("10"));

// 4. 用 AndAlso 连接(对应 &&)
var and1 = Expression.AndAlso(idEqual, nameEquals);
var finalExp = Expression.AndAlso(and1, ageGreater);

Expression<Func<People, bool>> lambda =
    Expression.Lambda<Func<People, bool>>(finalExp, paramC);

技巧:遇到复杂表达式,可以先用快捷 Lambda 写出来,再用 ILSpy 等反编译工具查看中间语言,照着拼装。


三、表达式访问者(ExpressionVisitor)

3.1 核心机制

ExpressionVisitor 是遍历和修改表达式目录树的工具,采用访问者模式。

  • Visit(expression):入口方法,判断节点类型后分发到对应的 VisitXxx 方法
  • 表达式树是二叉树结构,Visit 会递归遍历到所有叶节点
  • 重写 VisitXxx 方法可以在任意节点拦截、读取或修改内容
  • 返回新节点即可修改表达式树,返回原节点则保持不变

3.2 修改表达式树(OperationsVisitor)

通过重写 VisitBinary,可以在遍历时把加法替换成减法、乘法替换成除法:

public class OperationsVisitor : ExpressionVisitor
{
    public Expression Modify(Expression expression)
    {
        return Visit(expression);
    }

    protected override Expression VisitBinary(BinaryExpression b)
    {
        if (b.NodeType == ExpressionType.Add)
        {
            Expression left = Visit(b.Left);
            Expression right = Visit(b.Right);
            return Expression.Subtract(left, right); // 加法 → 减法
        }
        if (b.NodeType == ExpressionType.Multiply)
        {
            Expression left = Visit(b.Left);
            Expression right = Visit(b.Right);
            return Expression.Divide(left, right); // 乘法 → 除法
        }
        return base.VisitBinary(b);
    }
}

// 使用
Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;
var visitor = new OperationsVisitor();
Expression modified = visitor.Modify(exp);
// 原来 m * n + 2,修改后变成 m / n - 2

3.3 解析表达式为 SQL(ConditionBuilderVisitor)

用栈来收集每个节点转换出的 SQL 片段,最终拼接成完整条件:

public class ConditionBuilderVisitor : ExpressionVisitor
{
    private Stack<string> _StringStack = new Stack<string>();

    public string Condition()
    {
        string condition = string.Concat(_StringStack.ToArray());
        _StringStack.Clear();
        return condition;
    }

    // 二元表达式:a > b、a && b 等
    protected override Expression VisitBinary(BinaryExpression node)
    {
        _StringStack.Push(")");
        Visit(node.Right);
        // ToSqlOperator() 是自定义扩展方法,将 ExpressionType 映射为 SQL 运算符
        // 如 AndAlso/And → "AND",Equal → "=",GreaterThan → ">" 等(见本章末尾映射表)
        _StringStack.Push(" " + node.NodeType.ToSqlOperator() + " ");
        Visit(node.Left);
        _StringStack.Push("(");
        return node;
    }

    // 成员访问:c.Age、c.Name
    // 注意:如果是外部变量(闭包捕获),需要通过反射取值,而不是直接输出字段名
    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Expression is ConstantExpression)
        {
            // 外部变量:通过反射获取实际值
            var obj = (node.Expression as ConstantExpression).Value;
            var value = (node.Member as FieldInfo).GetValue(obj);
            _StringStack.Push($"'{value}'");
        }
        else
        {
            // 实体属性:输出列名
            _StringStack.Push($" [{node.Member.Name}] ");
        }
        return node;
    }

    // 常量:10、"abc"
    protected override Expression VisitConstant(ConstantExpression node)
    {
        _StringStack.Push($" '{node.Value}' ");
        return node;
    }

    // 方法调用:Contains、StartsWith、EndsWith
    protected override Expression VisitMethodCall(MethodCallExpression m)
    {
        string format = m.Method.Name switch
        {
            "StartsWith" => "({0} LIKE {1}+'%')",
            "Contains"   => "({0} LIKE '%'+{1}+'%')",
            "EndsWith"   => "({0} LIKE '%'+{1})",
            _ => throw new NotSupportedException(m.NodeType + " is not supported!")
        };
        Visit(m.Object);
        Visit(m.Arguments[0]);
        string right = _StringStack.Pop();
        string left = _StringStack.Pop();
        _StringStack.Push(string.Format(format, left, right));
        return m;
    }
}

ExpressionType 到 SQL 运算符的映射(SqlOperator):

ExpressionTypeSQL
AndAlso / AndAND
OrElse / OrOR
Equal=
NotEqual<>
GreaterThan>
GreaterThanOrEqual>=
LessThan<
LessThanOrEqual<=

使用示例:

Expression<Func<People, bool>> lambda = x =>
    x.Age > 5
    && x.Name.StartsWith("A")
    && x.Name.Contains("B")
    && x.Name.EndsWith("C");

var visitor = new ConditionBuilderVisitor();
visitor.Visit(lambda);
Console.WriteLine(visitor.Condition());
// 输出: ((( [Age] > '5') AND ([Name] LIKE 'A'+'%')) AND ([Name] LIKE '%'+'B'+'%')) AND ([Name] LIKE '%'+'C'))

外部变量捕获的处理:

string name = "AAA";
Expression<Func<People, bool>> lambda = x => x.Age > 5 && x.Name == name || x.Id > 5;
// name 是外部变量,编译后会被包装成 ConstantExpression 的字段
// VisitMember 中检测到 node.Expression is ConstantExpression,通过反射取出 "AAA"

3.4 实战扩展:BatchDelete

将表达式树解析为 SQL WHERE 条件,直接用于数据库操作:

public static void BatchDelete<T>(this IQueryable<T> entities, Expression<Func<T, bool>> expr)
{
    var visitor = new ConditionBuilderVisitor();
    visitor.Visit(expr);
    string condition = visitor.Condition();
    string sql = $"DELETE FROM [{typeof(T).Name}] WHERE {condition}";
    // 执行 sql...
}

// 使用
dbSet.BatchDelete<People>(p => p.Age > 30 && p.Name.StartsWith("A"));
// 生成: DELETE FROM [People] WHERE (( [Age] > '30') AND ([Name] LIKE 'A'+'%'))

四、表达式扩展:And / Or / Not

4.1 为什么需要参数替换

两个独立声明的表达式,参数对象不是同一个实例,直接合并会报错。需要用 ExpressionVisitor 统一替换成同一个参数:

Expression<Func<People, bool>> exp1 = x => x.Age > 5;  // 参数是 x
Expression<Func<People, bool>> exp2 = y => y.Id > 5;   // 参数是 y,不同实例

// 直接 Expression.AndAlso(exp1.Body, exp2.Body) 会出错
// 需要把两个 Body 中的参数都替换成同一个新参数 c

4.2 参数替换访问者(NewExpressionVisitor)

internal class NewExpressionVisitor : ExpressionVisitor
{
    private readonly ParameterExpression _newParameter;

    public NewExpressionVisitor(ParameterExpression param)
    {
        _newParameter = param;
    }

    public Expression Replace(Expression exp) => Visit(exp);

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return _newParameter; // 把所有参数节点替换成新参数
    }
}

4.3 And / Or / Not 扩展方法

public static class ExpressionExtend
{
    public static Expression<Func<T, bool>> And<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        if (expr1 == null) return expr2;
        if (expr2 == null) return expr1;

        ParameterExpression param = Expression.Parameter(typeof(T), "c");
        var visitor = new NewExpressionVisitor(param);
        var left = visitor.Replace(expr1.Body);
        var right = visitor.Replace(expr2.Body);
        var body = Expression.And(left, right); // 位运算 &,非短路
        return Expression.Lambda<Func<T, bool>>(body, param);
    }

    public static Expression<Func<T, bool>> Or<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        ParameterExpression param = Expression.Parameter(typeof(T), "c");
        var visitor = new NewExpressionVisitor(param);
        var left = visitor.Replace(expr1.Body);
        var right = visitor.Replace(expr2.Body);
        var body = Expression.Or(left, right); // 位运算 |,非短路
        return Expression.Lambda<Func<T, bool>>(body, param);
    }

    public static Expression<Func<T, bool>> Not<T>(
        this Expression<Func<T, bool>> expr)
    {
        var body = Expression.Not(expr.Body);
        return Expression.Lambda<Func<T, bool>>(body, expr.Parameters[0]);
    }
}

注意:Expression.And 对应位运算 &(非短路),Expression.AndAlso 对应 &&(短路)。源码中 And 扩展方法用的是 Expression.And,Or 用的是 Expression.Or。

使用示例:

Expression<Func<People, bool>> exp1 = x => x.Age > 5;
Expression<Func<People, bool>> exp2 = x => x.Id > 5;

var andExp = exp1.And(exp2);  // c => c.Age > 5 & c.Id > 5
var orExp  = exp1.Or(exp2);   // c => c.Age > 5 | c.Id > 5
var notExp = exp1.Not();      // x => !(x.Age > 5)

// 动态构建查询条件
Expression<Func<People, bool>> query = null;
string name = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(name))
    query = query.And(p => p.Name.Contains(name));

string ageStr = Console.ReadLine();
if (int.TryParse(ageStr, out int age))
    query = query.And(p => p.Age > age);

var result = dbSet.Where(query).ToList();

五、对象映射(高性能 Mapper)

5.1 五种方案对比

方案性能灵活性说明
硬编码⭐⭐⭐⭐⭐⭐手写赋值,不通用
反射(ReflectionMapper)⭐⭐⭐⭐⭐⭐每次都要反射,慢
序列化(SerializeMapper)⭐⭐⭐⭐⭐JSON 序列化再反序列化,慢
表达式树+字典缓存(ExpressionMapper)⭐⭐⭐⭐⭐⭐⭐⭐⭐首次拼装,后续走字典取委托
表达式树+泛型缓存(ExpressionGenericMapper)⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐最优,泛型副本天然隔离

5.2 反射方案(最慢)

public static TOut Trans<TIn, TOut>(TIn tIn)
{
    TOut tOut = Activator.CreateInstance<TOut>();
    foreach (var prop in tOut.GetType().GetProperties())
    {
        var sourceProp = tIn.GetType().GetProperty(prop.Name);
        prop.SetValue(tOut, sourceProp.GetValue(tIn));
    }
    foreach (var field in tOut.GetType().GetFields())
    {
        var sourceField = tIn.GetType().GetField(field.Name);
        field.SetValue(tOut, sourceField.GetValue(tIn));
    }
    return tOut;
}

5.3 序列化方案(慢)

public static TOut Trans<TIn, TOut>(TIn tIn)
{
    string json = JsonConvert.SerializeObject(tIn);
    return JsonConvert.DeserializeObject<TOut>(json);
}

5.4 表达式树+字典缓存(ExpressionMapper)

用 Dictionary<string, object> 缓存编译好的委托,key 是类型组合字符串。首次调用拼装并编译,后续直接从字典取委托执行:

private static Dictionary<string, object> _Dic = new Dictionary<string, object>();

public static TOut Trans<TIn, TOut>(TIn tIn)
{
    string key = $"funckey_{typeof(TIn).FullName}_{typeof(TOut).FullName}";
    if (!_Dic.ContainsKey(key))
    {
        // 拼装表达式:p => new TOut { Prop1 = p.Prop1, ... }
        ParameterExpression param = Expression.Parameter(typeof(TIn), "p");
        List<MemberBinding> bindings = new List<MemberBinding>();

        foreach (var prop in typeof(TOut).GetProperties())
        {
            var sourceProp = Expression.Property(param, typeof(TIn).GetProperty(prop.Name));
            bindings.Add(Expression.Bind(prop, sourceProp));
        }
        foreach (var field in typeof(TOut).GetFields())
        {
            var sourceField = Expression.Field(param, typeof(TIn).GetField(field.Name));
            bindings.Add(Expression.Bind(field, sourceField));
        }

        var memberInit = Expression.MemberInit(Expression.New(typeof(TOut)), bindings.ToArray());
        var lambda = Expression.Lambda<Func<TIn, TOut>>(memberInit, param);
        _Dic[key] = lambda.Compile();
    }
    return ((Func<TIn, TOut>)_Dic[key]).Invoke(tIn);
}

注意:_Dic 的 ContainsKey + 写入不是原子操作,多线程并发时存在竞态条件。生产环境建议改用 ConcurrentDictionary 或加锁,或直接使用泛型缓存方案。

5.5 表达式树+泛型缓存(ExpressionGenericMapper,最优)

泛型类的静态字段天然为每组类型生成独立副本,无需字典查找,性能更高:

public class ExpressionGenericMapper<TIn, TOut>
{
    private static readonly Func<TIn, TOut> _FUNC;

    static ExpressionGenericMapper()
    {
        ParameterExpression param = Expression.Parameter(typeof(TIn), "p");
        List<MemberBinding> bindings = new List<MemberBinding>();

        // 处理属性
        foreach (var prop in typeof(TOut).GetProperties())
        {
            var sourceProp = Expression.Property(param, typeof(TIn).GetProperty(prop.Name));
            bindings.Add(Expression.Bind(prop, sourceProp));
        }
        // 处理字段
        foreach (var field in typeof(TOut).GetFields())
        {
            var sourceField = Expression.Field(param, typeof(TIn).GetField(field.Name));
            bindings.Add(Expression.Bind(field, sourceField));
        }

        var memberInit = Expression.MemberInit(Expression.New(typeof(TOut)), bindings.ToArray());
        var lambda = Expression.Lambda<Func<TIn, TOut>>(memberInit, param);
        _FUNC = lambda.Compile(); // 只执行一次
    }

    public static TOut Trans(TIn t) => _FUNC(t);
}

// 使用
PeopleCopy copy = ExpressionGenericMapper<People, PeopleCopy>.Trans(people);

泛型缓存的原理: ExpressionGenericMapper<People, PeopleCopy> 和 ExpressionGenericMapper<User, UserDto> 是两个不同的类,各自有独立的静态字段 _FUNC,CLR 保证每组类型只初始化一次。


六、核心知识点总结

6.1 为什么要用表达式目录树?

  • 动态性:在运行时动态构建查询逻辑,把程序写活
  • 可分析性:可以解析表达式结构,转换为 SQL、MongoDB 查询、ES DSL 等
  • 类型安全:编译时检查,避免字符串拼接 SQL 的错误和注入风险
  • 高性能:配合缓存,性能接近硬编码

6.2 常用表达式节点速查

类型说明创建示例
ConstantExpression常量Expression.Constant(10)
ParameterExpression参数Expression.Parameter(typeof(int), "x")
BinaryExpression二元运算(加减乘除、比较、逻辑)Expression.Add(a, b) / Expression.GreaterThan(a, b)
MemberExpression成员访问(属性/字段)Expression.Property(obj, "Name") / Expression.Field(obj, fi)
MethodCallExpression方法调用Expression.Call(obj, method, args)
MemberInitExpression对象初始化Expression.MemberInit(newExp, bindings)
LambdaExpressionLambda 整体Expression.Lambda<Func<...>>(body, params)

6.3 实际应用场景

  • ORM 框架:Entity Framework、Dapper 等将 LINQ 表达式树解析为 SQL
  • 动态查询:根据用户输入拼装 WHERE 条件,替代字符串拼接 SQL
  • 对象映射:AutoMapper 底层原理,动态生成硬编码级别的映射委托
  • 规则引擎:运行时动态构建业务规则
  • 批量操作扩展:如 BatchDelete<T> 直接从表达式生成 DELETE SQL
  • 代码生成:运行时动态生成高性能执行逻辑

6.4 学习建议

  • 理解本质:表达式树是数据结构,不是可执行代码
  • 从简单开始:先掌握基础拼装,再学习复杂场景
  • 善用反编译:用 ILSpy 等工具查看快捷 Lambda 编译后的中间语言,照着拼装
  • 性能意识:合理使用缓存,避免重复拼装;优先选泛型缓存而非字典缓存

记住:表达式目录树 = 动态 + 类型安全 + 高性能

C# Modbus通信从入门到精通(8)——Modbus RTU(0x06功能码与C#代码实现) 本文详细介绍了ModbusRTU协议中0x06写单个寄存器功能码的原理、发送报文、返回报文、C#代码实现,由高级工程师多年经验总结而成,绝对干货! 阅读详情

相关推荐

C# Modbus通信从入门到精通(18)——Modbus ASCII(0x06功能码与C#代码实现)

本文详细介绍了ModbusASCII协议中0x06写单个寄存器功能码的原理、发送报文、返回报文、C#代码实现,由高级工程师多年经验总结而成,绝对干货!

qq_34059233的博客 1127

06C#类

C#类 1.2 类的继承 在1.3节,定义了一个描述个人情况的类Person,如果我们需要定义一个雇员类,当然可以从头开始定义雇员类Employee。但这样不能利用Person类中已定义的函数和数据。比较好的方法是,以Person类为基类,派生出一个雇员类Employee,雇员类Employee继承了Person类的数据成员和函数成员,既Person类的数据成员和函数成员成为Em...

weixin_30341735的博客 212

【C#】题库06:编程题

【C#】题库06:编程题1. 编一个程序,定义常量Pi=3.14159265,从键盘上输入半径r,求出圆的面积。2. 编一个程序,定义三个double类型的变量,分别从键盘上输入值给它们,然后用Console.WriteLine方法把它们输出成一列,小数点对齐,保留3位小数。3. 编一个程序,从键盘上输入三个数,用三元运算符(? :)把最大数找出来。4. 编一个程序,从键盘上输入三个数,用三元运算符(? :)把最小数找出来。5. 编一个程序,输入一个字符,如果是大写字母,就转换成小写字母,否则不转换。6.

敲代码两年半的练习生博客 571

T06_unity人物根物体动画 c#

找到刚创建的动画RootDownAnimation之后将他的loop time 对勾去掉。先调时间轴确定时间的长度,1.30为1.5s(90代表1.5s 1s = 60 贞)点击连接线,看右侧属性——has exit time(有退出时间吗)将对勾去掉。如果想把开门改成2s ,拖动最上面的小点按住移至2s即可。在门再创建一个触发器,门本身有个触发器是为了阻挡玩家穿过。点击create按钮,开始创建动画,创建动画文件夹,接下来我们要做一个门动画,需要手动搭建一个门。将CloseDoor属性面板1改为-1。

weixin_69360830的博客 280

uniapp实现app版本发现新版本后,自动更新

app实现自动更新

oldolder的博客 1282

eMMC数据擦除的方式

接下来主要是讲解erase、trim、discard的差异,涉及的command有CMD35、CMD36、CMD38。一般用来擦除整卡或者某些分区,如果只想擦除一个。以下是device status的部分解释。eMMC的数据擦除方式有6种,分别是。将会跳过写保护,只执行其他的范围。Discard的命令序列是。Erase的命令序列是。的范围有包含写保护,Trim的命令序列是。

DPJimmy的博客 1763

06 C#中的方法

1.方法的结构 方法是一块具有名称的代码。方法头+方法体方法头:指定方法的特征,返回类型,方法名称方法体:可执行代码的语句序列2.方法体内部的代码执行 方法体是一个块,是大括号括括起来的语句序列。包含:局部变量,控制流结构,方法调用,内嵌的块,其它方法(局部函数)3. 局部变量 局部变量经常用于保存局部的或临时的数据。字段通常是保存和对象状态有关的数据。声明一个局部变量:Type Identifier = Value;( 类型 变量名称,可选的初始化语句)局部变量的生存周期仅限于创建它的块及其内嵌的块。在方

flysh05的专栏 1028

Android应用加固脱壳实战:从爱加密原理到Frida内存Dump与修复

在移动应用安全领域,代码加固与动态加载是保护核心逻辑的关键技术。其原理在于通过修改应用启动流程,将原始DEX加密隐藏,并注入包含解密逻辑的壳DEX,实现运行时动态加载。这项技术的核心价值在于防止应用被逆向分析、二次打包或恶意篡改,广泛应用于金融、游戏等高安全要求场景。对于安全研究、漏洞挖掘或历史版本审计,掌握脱壳技能至关重要。本文聚焦于**爱加密**等主流加固方案,深入探讨如何利用**Frida**框架在内存中精准捕获解密后的DEX数据,并解决脱壳后常见的签名校验等运行问题,提供一套从原理到实践的完整攻防思

weixin_28716443的博客 489

C#学习教程06——继承

C#学习教程,适合新手小白,附带学习源码。

Kint的博客 1278

autodl云服务器使用中的conda源报错

通过find path -name filename找到.condarc文件,在autodl服务器中,一般在/root/.condarc。为了利用jypyter打开conda虚拟环境运行,需要下载ipykenel。这个报错是源的问题,在通过源获取包元数据时出现了HTTP 429错误。通过conda换源解决。修改.condarc文件,命令: vim .condarc。但这样还不能解决问题,得把报错中有问题的源注释掉。通过以下命令添加清华镜像源。把报错的链接源注释如下。

Ty_kilig的博客 1584

SMART200如何实现多次调用子程序带定时器功能

1、西门子200PLC的子程序由于很多限制条件,如果希望建立一个可以多次调用的子程序,那么这个子程序里是不能含有定时器指令的,所以我们只能退而求其次了。建议将沿指令(上升沿、下降沿)放在多次调用子程序的外面。 2、可以提供不同的时基,提供不同时间的定时 3、实现秒定时 5、子程序内部: STL代码: 1、时基信号由外部提供、 可以提供不同的时基信号 2、实现循环定时开关 LD #Start:L0....

RXXW_Dor的博客 1万+

C# WinForm —— 06 常用控件

参考:2023年C#之WinForm零基础教程50讲

ChanMon的博客 729

深度学习实战35-图卷积神经网络GCN模型的搭建与应用

大家好,我是微学AI,今天给大家介绍一下深度学习实战35-图卷积神经网络GCN模型的搭建与应用,GCN是一种用于处理图结构数据的深度学习模型,它可以捕捉节点之间的拓扑关系和局部特征,并进行图级别的预测和分类任务。下面将介绍如何搭建并应用GCN模型。

微学AI的博客 2224

06day c#判断语句

c#判断语句 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace _06day判断语句 8 { 9 cl...

weixin_30546189的博客 106

FAST-LIVO2 源码精读(五):IMU 运动模型、离散传播与去畸变原理

本文是「FAST-LIVO2 激光-惯性-视觉里程计源码精读」专栏第五篇,数学基础篇章(第 4–7 篇)的第二篇。上一篇在 SO(3) 流形上建立了姿态表示与右扰动模型;本篇聚焦 IMU 预积分的核心问题:一帧激光扫描横跨约 100 ms,帧内每一个点的采集时刻各不相同,如何利用 200 Hz 的 IMU 量测将全帧点云统一对齐到帧末时刻?答案完整封装在(587 行)的函数中,本篇逐段拆解其四阶段逻辑,并推导协方差矩阵随误差状态转移矩阵F_x逐帧演化的物理含义。

PX4-SLAM-PNC 156

❤️爆肝十二万字《python从零到精通教程》,从零教你变大佬❤️(建议收藏)

文章目录强烈推荐系列教程,建议学起来!!一.pycharm下载安装二.python下载安装三.pycharm上配置python四.配置镜像源让你下载嗖嗖的快4.1pycharm内部配置4.2手动添加镜像源4.3永久配置镜像源五.插件安装(比如汉化?)5.1自动补码神器第一款5.2汉化pycharm5.3其它插件六.美女背景七.自定义脚本开头八、这个前言一定要看九、python入门十、python缩进十一、Python注释1.单行注释2.多行注释十二、Python 变量1.变量定义理解2. 变量名命名3. 分

全栈川川 26万+

C#高级 06 文件操作

C#常见的实用的文件操作

younger_to_older的博客 856

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

06:在Lua中new C#对象

01:new C# 在 C#这样 new 一个对象: var newGameObj = new UnityEngine.GameObject(); 对应到 Lua 是这样: local newGameObj = CS.UnityEngine.GameObject() 基本类似,除了: 1、lua 里头没有 new 关键字; 2、所有 C#相关的都放到 CS 下,包括构造函数,静态成员属性、方法; ...

weixin_33950757的博客 2006

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

C# 06/19/2020

学习C#的第四天 字符串的修剪 补 - Trim("abcd".ToCharArray); - 删除字符串中出现的a, b, c, d string web = "#%%%%%####https://www.51zxw.net%%%%###%##%#%#%"; Console.WriteLine(web.Trim("#%".ToCharArray())); 字符串的复制 - string.Copy(要复制的字符串); - 静态方法

Fazerious的博客 190

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

C#笔记-06

交错数组 每个元素都为一维数组 using System; namespace day6 { class Program { static void Main(string[] args) { // 交错数组 参数数组 数据类型 int[][] array01; // 创建具有4个元素的交错数组 array01 = new int[4][];

qq_37609345的博客 244

C# 06/23/2020

学习C#的第八天 类的多态 - 虚方法 virtual - 将父类的方法标记为虚方法 - 方法可以在子类中重写 override namespace test { class Program { static void Main(string[] args) { Clerk mC = new Clerk(); Manger mM = new Manger(); //Manger

Fazerious的博客 194

C#笔记进阶篇06 委托

C#笔记进阶篇06 委托 ——本系列是基于人民邮电出版社《C#2008 C#图解教程》、清华大学出版社《C#入门经典(第五版)》两本书的自学C#笔记,如果您发现了本文的纰漏,还望不吝指正。 写在前边 所有的委托(Delegate)都派生自System.Delegate类 委托是一种可以把引用存储为函数的类型 委托可以让我们把函数当做变量去使用 委托解决了很多代码冗余的问题,也解决了方法回调的问题 1. 声明委托类型 委托的声明非常类似于函数,但不带函数体,且要使用delegate关键字。委托的声明指定了

Tervor_HAN的博客 334

C#学习Day06

存储结构:基于哈希函数,将键映射到存储桶(bucket)中,每个桶包含一个链表,用于处理哈希冲突。存储结构:双向链表,每个节点包含一个数据元素和两个指针,分别指向前一个和后一个节点。作用:用于频繁插入和删除操作的场景,可以在任意位置高效插入和删除元素。作用:用于存储相同类型的元素,提供动态大小和便捷的插入、删除操作。存储结构:数组是固定大小的线性数据结构,存储在连续的内存块中。作用:用于存储相同类型的元素,可以通过索引快速访问元素。存储结构:基于数组的动态数组,大小可以根据需求动态调整。

ycb1746147428的博客 482

C#语言基础速成Day06

昨天我们学习了C#面向对象和高级特性:,今天我们来学习C#属性访问器、方法参数和C#字符串,StringBuilder的一些内容。string适用于不需要频繁修改内容的场景,因为每次修改都会创建一个新的字符串对象。适用于需要频繁修改字符串内容的场景,因为它是可变的,可以直接修改而不会创建新的对象,从而提高性能。今天就分享到这里,我们明天见·~~~~~~~哦,对了。我们C#语言的基础部分就快要结束了,后面还有更加深奥的知识等着我们。前面的区域以后再来探索吧~

2301_79175212的博客 1256

【JVM】对象内存布局

HotSpot JVM 中,Java 对象内存布局由对象头(12字节,含Mark Word与压缩Klass Pointer)、实例数据及对齐填充构成,总大小为8字节倍数。对象头支持锁状态动态复用,指针压缩使引用仅4字节,但堆超32GB时失效。实例字段按类型优先级重排序以减少间隙,最小对象大小为16字节。通过JOL工具可精准验证布局,理解锁机制、GC、伪共享等底层行为。

2301_79732484的博客 140
上一篇: 07-C#
下一篇: 10-C#
似水明俊德
博客等级 码龄8年 247粉丝 276原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值