C# C语言中基于规则的可配置系统#
我有一个算法,它根据给算法的两个参数返回分类(字符串)列表:一个类型变量和一个额外的类别字符串,允许将某些特殊分类添加到结果列表中 由于将规则表示为ifs和switch语句,因此当前实现无法读取且无法扩展。规则也是硬编码的 代码的简化版本:C# C语言中基于规则的可配置系统#,c#,f#,pattern-matching,rules,C#,F#,Pattern Matching,Rules,我有一个算法,它根据给算法的两个参数返回分类(字符串)列表:一个类型变量和一个额外的类别字符串,允许将某些特殊分类添加到结果列表中 由于将规则表示为ifs和switch语句,因此当前实现无法读取且无法扩展。规则也是硬编码的 代码的简化版本: private static List<string> DetermineTypes(Type x, object category) { List<string> Types = new List<string>(
private static List<string> DetermineTypes(Type x, object category) {
List<string> Types = new List<string>();
if (category is DateTime) {
types.Add("1");
types.Add("2");
types.Add("3");
} else if (category is string) {
switch ((string)category) {
case "A":
Types.Add("4");
break;
case "B":
case "C":
case "D":
Types.Add("5");
break;
case "":
Types = DetermineTypesFromX(Types, x);
break;
default:
Types.Add("6");
break;
}
}
return graphTypes;
}
private static List<string> DetermineTypesFromX(List<string> Types, Type x) {
if (x.Equals(typeof(int))) {
Types.Add("7");
} else if (x.Equals(typeof(double))) {
Types.Add("8");
} else if (x.Equals(typeof(System.DateTime))) {
Types.Add("9");
Types.Add("10");
}
return Types;
}
私有静态列表DetermineTypes(类型x,对象类别){
列表类型=新列表();
如果(类别为日期时间){
类型。添加(“1”);
类型。添加(“2”);
类型。添加(“3”);
}else if(类别为字符串){
开关((字符串)类别){
案例“A”:
类型。添加(“4”);
打破
案例“B”:
案例“C”:
案例“D”:
类型。添加(“5”);
打破
案例“”:
类型=DetermineTypesFromX(类型,x);
打破
违约:
类型。添加(“6”);
打破
}
}
返回图形类型;
}
私有静态列表DetermineTypesFromX(列表类型,类型x){
如果(x.Equals(typeof(int))){
类型。添加(“7”);
}如果(x.Equals(typeof(double)),则为else{
类型。添加(“8”);
}else如果(x.Equals(typeof(System.DateTime))){
类型。添加(“9”);
类型。添加(“10”);
}
返回类型;
}
我认为最好用xml指定这些类型,这样新的类型/规则就不需要更改代码,但在这种情况下,这可能太重了。基本上,我试图解决一个问题,即可以随时添加一个新的“类型”:常见的情况是,它是上面的“规则”之一,并且不太可能需要添加一个新的“规则”分支
我仍然要确定,与边缘案例发生的可能性和业务环境(时间表等)相比,使用xml定义的规则(或任何其他方式)使其完全动态化所需的工作是否值得
但我的主要问题是如何优雅地简化上面的嵌套条件代码?也许在设计中加入更多的灵活性以提高可扩展性
我想知道结合使用F#模式匹配是否是合适的解决方案?(注意:以前从未使用过F#,最近一直很好奇,所以我才这么问)最近在以下两篇博文中讨论了一种称为分派表的模式,您可能会感兴趣:
我建议你看看发动机。NxBRE周围有一个很好的社区,而且相当成熟。这可能超出了您当前的需求,但如果您希望这些规则随着时间的推移变得更加复杂,BRE将提供一个很好的框架来控制事情 我不会回避基于配置的选项;它通常具有不需要重建的优点。如果您不想这样做,另一个选项可能是通过属性创建类型元数据。这使得为新类型(您编写的)添加数据变得很简单,并且您可以通过
TypeDescriptor.AddAttributes
(间接地)向现有类型(int
等)添加属性,只要您使用TypeDescriptor.GetAttributes
再次将它们取出-p
不管这是不是一个好主意。。。好的,反射(和孪生的,TypeDescriptor
)可能会很慢,所以如果你想在一个紧密的循环中使用它,我会先看一些涉及字典的东西。既然你提到了F#,下面是一些行为与C#代码非常相似的F#代码:
开放系统
设DetermineTypesFromX(x:Type)=
如果x等于(typeof),则
["7"]
elif x等于(typeof)那么
["8"]
elif x等于(typeof)那么
["9"; "10"]
其他的
[]
let DetermineTypes(x:Type,category:obj)=
将类别与
| :? 日期时间->[“1”;“2”;“3”]
| :? 字符串为s->
匹配
|“A”->[“4”]
|“B”|“C”|“D”->[“5”]
|“”->DetermineTypesFromX(x)
| _ -> ["6"]
| _ -> []
也就是说,我建议考虑使用表驱动方法作为硬编码if/开关逻辑的替代方法,而不管您是否将逻辑移出代码并放入配置文件。您的问题可能是根据或编码的 此外,Chris Smith的博客中也有关于决策树的帖子: 及
我遇到过类似的情况,之前我就类似的问题问了一些问题,这些问题可能会对您有所帮助 我所做的系统是一个配置驱动、基于规则的动态系统。所有配置和规则都保存在数据库中。根据从数据库中检索到的值和规则动态构造决策表。然后用C#转换和比较数值。这是我问的问题。从数据库中检索到的数据 因此,在配置表方面,我最终得到了与此类似的东西(只是一个示例):
条件为决策LHS操作员RHS
TTFF假邮政编码>100
TFTF假邮政编码<10000
FTTT为真
注意:LHS是对象的属性名称。
上表以通俗易懂的英语书写:
Condition 1 PostCode > 100 Yes Yes No No
Condition 2 PostCode < 10000 Yes No Yes No
Outcome 1 Yes
Outcome 2 Yes
Outcome 3 Yes
Outcome 4 Yes
Then you have other tables/configs to determine the action for each outcome.
条件1邮政编码>100是否
条件2邮政编码<10000是否是否
结果1是的
结果2是的
结果3是的
结果4是的
然后,您可以使用其他表/配置来确定每个结果的操作。
实现的核心部分是如何动态构造决策表以及如何动态转换和比较字符串值,所有这些我都在上面的段落中提供了指向特定实现的链接。我相信你可以在你的情况下应用类似的概念,我希望我已经大致解释了这个概念
Conditions IsDecision LHS Operator RHS
TTFF False PostCode > 100
TFTF False PostCode < 10000
FTTT True
Note: LHS is the property name of the object.
Condition 1 PostCode > 100 Yes Yes No No
Condition 2 PostCode < 10000 Yes No Yes No
Outcome 1 Yes
Outcome 2 Yes
Outcome 3 Yes
Outcome 4 Yes
Then you have other tables/configs to determine the action for each outcome.