Files
Gen_Hack-and-Slash-Roguelit…/Client/Assets/Scripts/Utils/Resolver.cs
2025-07-21 13:58:58 +08:00

131 lines
6.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Linq;
using System.Linq.Expressions;
namespace Utils
{
public static class Resolver
{
/// <summary>
/// 将字符串表达式解析为一个谓词函数,该函数可以用于筛选实体对象。
/// </summary>
/// <param name="expression">表示条件的字符串表达式。格式示例:"entity.Id &gt; 10" 或 "entity.Name == 'John'"。</param>
/// <returns>返回一个 Func&lt;Entity.Entity, bool&gt; 类型的委托,表示解析后的谓词函数。</returns>
/// <exception cref="FormatException">当输入表达式的格式不正确时抛出此异常。</exception>
/// <exception cref="NotSupportedException">当表达式中包含不支持的操作符或数据类型时抛出此异常。</exception>
/// <remarks>
/// 表达式的格式必须符合以下规则:
/// - 表达式由三部分组成:属性路径、操作符和值,用空格分隔。
/// - 属性路径格式为 "entity.PropertyName",其中 PropertyName 是实体类中的一个公共属性或字段。
/// - 操作符可以是以下之一:"&gt;", "&lt;", "&gt;=", "&lt;=", "==", "!="。
/// - 值的类型必须与属性的类型匹配并且支持以下类型string, int, long, float, double, decimal, bool, DateTime, Guid 和枚举类型。
///
/// 注意事项:
/// - 字符串值需要用单引号或双引号括起来,例如 'John' 或 "John"。
/// - 对于可为空类型Nullable会自动处理其底层类型的转换。
/// - 字符串比较默认使用不区分大小写的 Equals 方法。
/// </remarks>
public static Func<Entity.Entity, bool> ParsePredicate(string expression)
{
// 格式示例:"entity.Id > 10" 或 "entity.Name == 'John'"
var parts = expression.Split(new[] { ' ' }, 3, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 3)
throw new FormatException(
"Invalid expression format. Expected format: 'entity.Property Operator Value'");
// 解析属性和操作符
var propPath = parts[0].Split('.')[1]; // "Id" 或 "Name"
var op = parts[1]; // ">", "==" 等
// 创建表达式参数
var param = Expression.Parameter(typeof(Entity.Entity), "entity");
// 获取属性访问表达式
var propAccess = propPath.Split('.')
.Aggregate<string, Expression>(param, Expression.PropertyOrField);
// 获取属性类型
var propType = propAccess.Type;
// 解析值并转换为适当类型
object value;
var valueStr = parts[2].Trim();
try
{
if (propType == typeof(string))
// 处理字符串值(去除引号)
value = valueStr.Trim('\'', '"');
else if (propType == typeof(int))
value = int.Parse(valueStr);
else if (propType == typeof(long))
value = long.Parse(valueStr);
else if (propType == typeof(float))
value = float.Parse(valueStr);
else if (propType == typeof(double))
value = double.Parse(valueStr);
else if (propType == typeof(decimal))
value = decimal.Parse(valueStr);
else if (propType == typeof(bool))
value = bool.Parse(valueStr);
else if (propType == typeof(DateTime))
value = DateTime.Parse(valueStr);
else if (propType == typeof(Guid))
value = Guid.Parse(valueStr);
else if (propType.IsEnum)
value = Enum.Parse(propType, valueStr);
else
throw new NotSupportedException($"Type {propType.Name} is not supported");
}
catch (Exception ex)
{
throw new FormatException($"Failed to parse value '{valueStr}' for type {propType.Name}", ex);
}
// 创建常量表达式(确保类型匹配)
var constant = Expression.Constant(value, propType);
// 处理可为空类型的情况
if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
var underlyingType = Nullable.GetUnderlyingType(propType);
propAccess = Expression.Property(propAccess, "Value");
constant = Expression.Constant(Convert.ChangeType(value, underlyingType), underlyingType);
}
// 创建比较表达式
Expression comparison;
if (propType == typeof(string) && (op == "==" || op == "!="))
{
// 字符串特殊处理使用Equals方法进行不区分大小写的比较
var equalsMethod =
typeof(string).GetMethod("Equals", new[] { typeof(string), typeof(StringComparison) });
var methodCall = Expression.Call(
propAccess,
equalsMethod,
constant,
Expression.Constant(StringComparison.OrdinalIgnoreCase));
comparison = op == "==" ? methodCall : Expression.Not(methodCall);
}
else
{
// 其他类型使用标准二元运算符
comparison = op switch
{
">" => Expression.GreaterThan(propAccess, constant),
"<" => Expression.LessThan(propAccess, constant),
">=" => Expression.GreaterThanOrEqual(propAccess, constant),
"<=" => Expression.LessThanOrEqual(propAccess, constant),
"==" => Expression.Equal(propAccess, constant),
"!=" => Expression.NotEqual(propAccess, constant),
_ => throw new NotSupportedException($"Operator {op} not supported")
};
}
// 编译为委托
var lambda = Expression.Lambda<Func<Entity.Entity, bool>>(comparison, param);
return lambda.Compile();
}
}
}