动态 where 运算符 c#
Dynamic where operator c#
这个question12年前就有人问过,现在dotnet变化太大了,不知道现在有没有解决办法
我有一个代表我要执行的规则和运算符的模型:
public class RuleStatement
{
public string Path { get; set; }
public string Operator { get; set; }
public int Value { get; set; }
}
来源:
public class Foo
{
public string Path {get;set;}
public int Value {get;set;}
}
我想在源上执行此动态 where
运算符,因此只会返回与所有规则匹配的那些。示例:
List<Foo> source = new List<Foo>();
//Sample of source
source.Add(new Foo{Path="A", Value = 10});
source.Add(new Foo{Path="B", Value = 20});
source.Add(new Foo{Path="C", Value = 30});
//Scenario 1 => Expected to be true
List<RuleStatement> rules = new List<RuleStatement>();
rules.Add(new RuleStatement{Path="A", Operator = ">=", Value=10});
rules.Add(new RuleStatement{Path="B", Operator = "==", Value=20});
bool isMatch = rules.All(rule =>
source.Any(s => s.Path == rule.Path && s.Value $"{rule.Operator}" rule.Value));
//Scenario 2 => Expected to be false
List<RuleStatement> rules = new List<RuleStatement>();
rules.Add(new RuleStatement{Path="A", Operator = ">=", Value=100});
rules.Add(new RuleStatement{Path="B", Operator = "==", Value=20});
bool isMatch = rules.All(rule =>
source.Any(s => s.Path == rule.Path && s.Value $"{rule.Operator}" rule.Value));
我找不到任何可行的解决方案,那么是否可以在 .NET Core 5 上执行此动态 where
操作?
如果不可能,有什么解决方法可以解决这个问题?
你每天都能学到新东西。如果您真的想使用尽可能少的字符串和代码(并且性能无关紧要),您可以使用 NuGet 包 Microsoft.CodeAnalysis.Scripting
:
using Microsoft.CodeAnalysis.CSharp.Scripting;
s.Path == rule.Path && CSharpScript.EvaluateAsync<bool>($"{s.Value} {rule.Operator} {rule.Value}").Result;
如果性能更重要,那么也许这就是方法:
自 .Net Framework 3.5 以来,这已经成为可能,但不能直接使用字符串。但是你可以写一个函数来实现这个。对于您的情况,您将需要一个用于操作员的开关盒 - 缓存委托可能有意义。
using System.Linq.Expressions;
var param1 = Expression.Parameter(typeof(int));
var param2 = Expression.Parameter(typeof(int));
var expr = Expression.LessThan(param1, param2);
var func = Expression.Lambda<Func<int, int, bool>>(expr, param1, param2).Compile();
var result = func(1,2);
问题:
class 个元素的集合,定义为:
List<Foo> source = new List<Foo>();
public class Foo
{
public string Path {get;set;}
public int Value {get;set;}
}
需要与另一个 class 对象中定义的一组规则进行比较,定义为:
public class RuleStatement
{
public string Path { get; set; }
public string Operator { get; set; }
public int Value { get; set; }
}
要遵守 RuleStatement
对象指定的 规则 ,Foo
元素必须匹配 Path
属性 RuleStatement
和比较,应用于 Value
属性 并基于 Operator
属性,必须 return 是肯定的结果。
Operator
属性 定义为字符串,这使得基于 implicit/explicit 运算符的实现有些复杂。
Foo
元素是否符合指定规则的测试也是时间敏感的。
一个可能的解决方案是将 Operator
属性 值映射到一个 Func 委托,该委托根据运算符执行比较,可能还有一组特定于每个操作员。
词典通常用于此类任务。
在一个简单的形式中,它可以是 Dictionary<string, Func<int, int, bool>>
,因为比较了两个 int
值并且预期结果是 true/false
。
如果 Foo 和 RuleStatement
class 可以 modified/adapted,则实现也可以更 通用 。字典映射器也可以是 RuleStatement
class.
的一部分
例如,使 Foo class 实现可在类似情况下使用的接口:
public interface IGenericFoos
{
string Path { get; set; }
int Value { get; set; }
}
public class Foo : IGenericFoos
{
public string Path { get; set; }
public int Value { get; set; }
}
RuleStatement
class 可以更改为:
public class RuleStatement<T> where T : IGenericFoos
{
public static Dictionary<string, Func<T, RuleStatement<T>, bool>> operators =
new Dictionary<string, Func<T, RuleStatement<T>, bool>>() {
[">="] = (T, R) => T.Value >= R.Value,
["<="] = (T, R) => T.Value <= R.Value,
["<>"] = (T, R) => T.Value != R.Value,
["!="] = (T, R) => T.Value != R.Value,
["=="] = (T, R) => T.Value == R.Value,
["="] = (T, R) => T.Value == R.Value,
["<"] = (T, R) => T.Value < R.Value,
[">"] = (T, R) => T.Value > R.Value,
};
public string Path { get; set; }
public string Operator { get; set; }
public int Value { get; set; }
public bool Eval(T ifoo) => ifoo.Path == Path && operators[Operator](ifoo, this);
}
评估所有 Foo 对象是否符合一组规则的 LINQ 查询可以简化为:
var source = new List<Foo> { ... }
var rules = new List<RuleStatement<Foo>> { ... }
// [...]
bool isMatch = rules.All(rule => source.Any(s => rule.Eval(s)));
// Or implicitly:
bool isMatch = rules.All(rule => source.Any(rule.Eval));
可以添加其他类型的运算符来执行不同的比较或其他操作。例如,“+”和“-”运算符可以使用固定值进行评估,例如:
["+"] = (T, R) => T.Value + R.Value >= 0,
["-"] = (T, R) => T.Value - R.Value >= 0,
或使用变量值,将 属性(最终也是特定的重载构造函数)添加到 RuleStatement<T>
class.
如果将来可能需要更复杂的comparisons/operations,这种实施方式会提供更多灵活性。
如果无法修改这些 classes,则可以将字典用作独立的字段(或任何合适的字段),并且可以更改 LINQ 查询(保留 OP 中显示的原始定义)在:
bool isMatch = rules.All(rule => source
.Any(s => s.Path == rule.Path && operators[rule.Operator](s.Value, rule.Value)));
这个question12年前就有人问过,现在dotnet变化太大了,不知道现在有没有解决办法
我有一个代表我要执行的规则和运算符的模型:
public class RuleStatement
{
public string Path { get; set; }
public string Operator { get; set; }
public int Value { get; set; }
}
来源:
public class Foo
{
public string Path {get;set;}
public int Value {get;set;}
}
我想在源上执行此动态 where
运算符,因此只会返回与所有规则匹配的那些。示例:
List<Foo> source = new List<Foo>();
//Sample of source
source.Add(new Foo{Path="A", Value = 10});
source.Add(new Foo{Path="B", Value = 20});
source.Add(new Foo{Path="C", Value = 30});
//Scenario 1 => Expected to be true
List<RuleStatement> rules = new List<RuleStatement>();
rules.Add(new RuleStatement{Path="A", Operator = ">=", Value=10});
rules.Add(new RuleStatement{Path="B", Operator = "==", Value=20});
bool isMatch = rules.All(rule =>
source.Any(s => s.Path == rule.Path && s.Value $"{rule.Operator}" rule.Value));
//Scenario 2 => Expected to be false
List<RuleStatement> rules = new List<RuleStatement>();
rules.Add(new RuleStatement{Path="A", Operator = ">=", Value=100});
rules.Add(new RuleStatement{Path="B", Operator = "==", Value=20});
bool isMatch = rules.All(rule =>
source.Any(s => s.Path == rule.Path && s.Value $"{rule.Operator}" rule.Value));
我找不到任何可行的解决方案,那么是否可以在 .NET Core 5 上执行此动态 where
操作?
如果不可能,有什么解决方法可以解决这个问题?
你每天都能学到新东西。如果您真的想使用尽可能少的字符串和代码(并且性能无关紧要),您可以使用 NuGet 包 Microsoft.CodeAnalysis.Scripting
:
using Microsoft.CodeAnalysis.CSharp.Scripting;
s.Path == rule.Path && CSharpScript.EvaluateAsync<bool>($"{s.Value} {rule.Operator} {rule.Value}").Result;
如果性能更重要,那么也许这就是方法:
自 .Net Framework 3.5 以来,这已经成为可能,但不能直接使用字符串。但是你可以写一个函数来实现这个。对于您的情况,您将需要一个用于操作员的开关盒 - 缓存委托可能有意义。
using System.Linq.Expressions;
var param1 = Expression.Parameter(typeof(int));
var param2 = Expression.Parameter(typeof(int));
var expr = Expression.LessThan(param1, param2);
var func = Expression.Lambda<Func<int, int, bool>>(expr, param1, param2).Compile();
var result = func(1,2);
问题:
class 个元素的集合,定义为:
List<Foo> source = new List<Foo>();
public class Foo
{
public string Path {get;set;}
public int Value {get;set;}
}
需要与另一个 class 对象中定义的一组规则进行比较,定义为:
public class RuleStatement
{
public string Path { get; set; }
public string Operator { get; set; }
public int Value { get; set; }
}
要遵守 RuleStatement
对象指定的 规则 ,Foo
元素必须匹配 Path
属性 RuleStatement
和比较,应用于 Value
属性 并基于 Operator
属性,必须 return 是肯定的结果。
Operator
属性 定义为字符串,这使得基于 implicit/explicit 运算符的实现有些复杂。
Foo
元素是否符合指定规则的测试也是时间敏感的。
一个可能的解决方案是将 Operator
属性 值映射到一个 Func 委托,该委托根据运算符执行比较,可能还有一组特定于每个操作员。
词典通常用于此类任务。
在一个简单的形式中,它可以是 Dictionary<string, Func<int, int, bool>>
,因为比较了两个 int
值并且预期结果是 true/false
。
如果 Foo 和 RuleStatement
class 可以 modified/adapted,则实现也可以更 通用 。字典映射器也可以是 RuleStatement
class.
例如,使 Foo class 实现可在类似情况下使用的接口:
public interface IGenericFoos
{
string Path { get; set; }
int Value { get; set; }
}
public class Foo : IGenericFoos
{
public string Path { get; set; }
public int Value { get; set; }
}
RuleStatement
class 可以更改为:
public class RuleStatement<T> where T : IGenericFoos
{
public static Dictionary<string, Func<T, RuleStatement<T>, bool>> operators =
new Dictionary<string, Func<T, RuleStatement<T>, bool>>() {
[">="] = (T, R) => T.Value >= R.Value,
["<="] = (T, R) => T.Value <= R.Value,
["<>"] = (T, R) => T.Value != R.Value,
["!="] = (T, R) => T.Value != R.Value,
["=="] = (T, R) => T.Value == R.Value,
["="] = (T, R) => T.Value == R.Value,
["<"] = (T, R) => T.Value < R.Value,
[">"] = (T, R) => T.Value > R.Value,
};
public string Path { get; set; }
public string Operator { get; set; }
public int Value { get; set; }
public bool Eval(T ifoo) => ifoo.Path == Path && operators[Operator](ifoo, this);
}
评估所有 Foo 对象是否符合一组规则的 LINQ 查询可以简化为:
var source = new List<Foo> { ... }
var rules = new List<RuleStatement<Foo>> { ... }
// [...]
bool isMatch = rules.All(rule => source.Any(s => rule.Eval(s)));
// Or implicitly:
bool isMatch = rules.All(rule => source.Any(rule.Eval));
可以添加其他类型的运算符来执行不同的比较或其他操作。例如,“+”和“-”运算符可以使用固定值进行评估,例如:
["+"] = (T, R) => T.Value + R.Value >= 0,
["-"] = (T, R) => T.Value - R.Value >= 0,
或使用变量值,将 属性(最终也是特定的重载构造函数)添加到 RuleStatement<T>
class.
如果将来可能需要更复杂的comparisons/operations,这种实施方式会提供更多灵活性。
如果无法修改这些 classes,则可以将字典用作独立的字段(或任何合适的字段),并且可以更改 LINQ 查询(保留 OP 中显示的原始定义)在:
bool isMatch = rules.All(rule => source
.Any(s => s.Path == rule.Path && operators[rule.Operator](s.Value, rule.Value)));