使用表达式树实现字符串形式的表达式访问对象属性

使用表达式树实现字符串形式的表达式访问对象属性

在C#中,使用表达式树(Expression Trees)可以动态地构建和编译代码,这使得我们可以通过字符串形式的表达式来访问对象的属性。下面是实现这一功能的几种方法:

1. 基本实现方式

using System;
using System.Linq.Expressions;
using System.Reflection;

public static class PropertyAccessor
{
public static Func<T, object> CreatePropertyGetter<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T), "x");
Expression property = Expression.Property(parameter, propertyName);
Expression conversion = Expression.Convert(property, typeof(object));
return Expression.Lambda<Func<T, object>>(conversion, parameter).Compile();
}

public static Action<T, object> CreatePropertySetter<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T), "x");
var valueParameter = Expression.Parameter(typeof(object), "value");
var property = Expression.Property(parameter, propertyName);
var assignment = Expression.Assign(
property,
Expression.Convert(valueParameter, property.Type));
return Expression.Lambda<Action<T, object>>(assignment, parameter, valueParameter).Compile();
}
}

使用示例

public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}

// 使用
var person = new Person { Name = "Alice", Age = 30 };

// 获取属性
var getName = PropertyAccessor.CreatePropertyGetter<Person>("Name");
Console.WriteLine(getName(person)); // 输出 "Alice"

// 设置属性
var setAge = PropertyAccessor.CreatePropertySetter<Person>("Age");
setAge(person, 35);
Console.WriteLine(person.Age); // 输出 35

2. 支持嵌套属性访问

public static Func<T, object> CreateNestedPropertyGetter<T>(string propertyPath)
{
var parameter = Expression.Parameter(typeof(T), "x");
Expression property = parameter;

foreach (var member in propertyPath.Split('.'))
{
property = Expression.PropertyOrField(property, member);
}

Expression conversion = Expression.Convert(property, typeof(object));
return Expression.Lambda<Func<T, object>>(conversion, parameter).Compile();
}

使用示例

public class Address
{
public string City { get; set; }
}

public class Person
{
public string Name { get; set; }
public Address HomeAddress { get; set; }
}

// 使用
var person = new Person
{
Name = "Bob",
HomeAddress = new Address { City = "New York" }
};

var getCity = PropertyAccessor.CreateNestedPropertyGetter<Person>("HomeAddress.City");
Console.WriteLine(getCity(person)); // 输出 "New York"

3. 更完整的表达式解析

对于更复杂的表达式(如数组索引、方法调用等),可以使用 System.Linq.Dynamic.Core 库:

// 安装 NuGet 包: System.Linq.Dynamic.Core

using System.Linq.Dynamic.Core;

public static class DynamicAccessor
{
public static Func<T, object> CreateExpression<T>(string expression)
{
var parameter = Expression.Parameter(typeof(T), "x");
var lambda = DynamicExpressionParser.ParseLambda(
new[] { parameter },
typeof(object),
expression);
return (Func<T, object>)lambda.Compile();
}
}

使用示例

public class Company
{
public List<Person> Employees { get; set; } = new List<Person>();
}

// 使用
var company = new Company
{
Employees = new List<Person>
{
new Person { Name = "Alice", Age = 30 },
new Person { Name = "Bob", Age = 35 }
}
};

var getFirstEmployeeName = DynamicAccessor.CreateExpression<Company>("Employees.Name");
Console.WriteLine(getFirstEmployeeName(company)); // 输出 "Alice"

4. 性能考虑

表达式树在第一次构建和编译时会有性能开销,但编译后的委托执行速度与直接编写的代码几乎相同。如果需要在循环中重复使用,应该缓存编译后的委托。

private static Dictionary<string, Func<T, object>> _getterCache = new Dictionary<string, Func<T, object>>();

public static Func<T, object> GetCachedPropertyGetter<T>(string propertyName)
{
if (!_getterCache.TryGetValue(propertyName, out var getter))
{
getter = CreatePropertyGetter<T>(propertyName);
_getterCache[propertyName] = getter;
}
return getter;
}

注意事项

  1. 属性名称或路径错误会抛出异常,可以添加错误处理
  2. 对于私有成员,需要设置适当的绑定标志
  3. 表达式树不支持所有C#语法,复杂逻辑可能需要使用其他方法

以上方法提供了灵活的方式来通过字符串表达式访问对象属性,可以根据具体需求选择适合的实现方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值