C# - switch 表达式中的动态模式匹配案例?
C# - Dynamic pattern matching cases in switch expression?
我最近在将分支逻辑简化为 switch 表达式和利用模式匹配方面发现了很多价值。特别是在我有多个数据元素需要考虑的情况下。
return tuple switch
{
("Foo", "Bar") => "FooBar",
("Foo", _ ) => "Foo",
(_, "Bar") => "Bar",
(_, _) => "Default Value"
};
我对“元组”变量的任何给定值的预期行为如下:
var tuple = ("Hello", "World") -> "Default Value"
var tuple = ("Foo", "World") -> "Foo"
var tuple = ("Foo", "") -> "Foo"
var tuple = ("Foo", null) -> "Foo"
var tuple = ("Hello", "Bar") -> "Bar"
这一切都很好。
我最近发现了一种情况,我想要的是必须按照“最具体到最不具体”的顺序检查的一系列规则,如果值不存在,则使用合理的默认值。所以实际上与上面的模式匹配序列相同。但是,我需要我的最终用户能够自己配置模式,并且模式案例是动态的(即来自数据库 table)。
所以,给定这些数据记录:
Input1, Input2, ReturnValue
"Foo", "Bar", "FooBar"
"Foo", NULL, "Foo"
NULL, "Bar", "Bar"
NULL, NULL, "Default Value"
我想“生成”这些案例,就像我上面的硬编码示例一样。
("Foo", "Bar") => "FooBar",
("Foo", _ ) => "Foo",
(_, "Bar") => "Bar",
(_, _) => "Default Value"
然后,如果用户想要添加新的“规则”,他们会添加一条新记录
Input1, Input2, ReturnValue
"Hello","World", "I'm the super special return value".
这将创建以下模式案例:
("Hello", "World") => "I'm the super special return value",
计算时对应的结果为:
var tuple = ("Hello", "World") -> "I'm the super special return value"
var tuple = ("Hello", "Other") -> "Default Value"
在我看来,我想做一些事情:
var switchOptions = dataRecords.Select(record =>
{
var pattern = (record.Input1 ?? '_', record.Input2 ?? '_');
var func = (pattern) => record.Result;
return func;
});
//and then somehow build the switch expression out of these options.
为什么这不起作用有几个原因是有道理的,我肯定不限于:
- switch 表达式语法不是具有 AddPattern() 的对象。
- 表达式中的字符“_”和 _ 运算符不是一回事...
我想到的另一个选择是将记录集映射到字典中,其中 Key 是元组(列:Input1、Input2),值是预期的 return 值(列:返回值)。这个问题是,它没有提供任何能力通过简单的键查找将 NULL 数据库值视为丢弃模式。
归根结底,我的问题是: 我假设 switch 表达式语法只是对幕后更复杂实现的一些很好的糖分覆盖。我可以使用 C# 9 中已有的实现来实现“动态”开关表达式的想法吗?还是我找错了树,需要自己完全实现?
我不确定您是否在寻找某种 code-generation,但是通过一些 Linq 聚合,您可以将 patterns/records 放在一个序列中并将结果用作有点像 switch-expression 模式匹配的功能。重要的是 dataRecords
包含您希望它们被评估的顺序的记录:
public record Record(string Input1, string Input2, string ReturnValue);
public record Pattern(Func<string, string, bool> Predicate, string ReturnValue);
Pattern CreatePattern(Record rec)
{
return new (
(l, r) =>
(l == rec.Input1 || rec.Input1 == null)
&& (r == rec.Input2 || rec.Input2 == null),
rec.ReturnValue
);
}
// Create your pattern matching "switch-expression"
var switchExp = dataRecords
.Reverse()
.Select(CreatePattern)
.Aggregate<Pattern, Func<string, string, string>>(
(_, _) => null,
(next, pattern) =>
(l, r) => pattern.Predicate(l, r) ? pattern.ReturnValue : next(l, r)
);
switchExp("abc", "bar"); // "bar"
看到了in action.
您必须自己实施。 switch 模式匹配类似于常规使用的 switch case,需要编译时常量,并且很可能用跳转 table 实现。因此不能在运行时修改。
感觉您要实现的目标应该不会太难。像这样
PatternDict = new Dictionary<string, Dictionary<string, string>>();
PatternDict["_"] = new Dictionary<string, string>();
PatternDict["_"]["_"] = null;
更新码为:
Dictionary<string, string> dict;
if (!PatternDict.TryGetValue(input1, out dict)) {
dict = new Dictionary<string, string>();
dict["_"] = "default";
}
dict[input2] = returnValue;
PatternDict[input1] = dict;
检索码为:
Dictionary<string, string> dict;
if (!PatternDict.TryGetValue(input1, out dict)) {
dict = PatternDict["_"];
}
string returnVal;
if (!dict.TryGetValue(input2, out returnVal)) {
returnVal = dict["_"];
}
return returnVal;
如果您使用的新版本的 C# 支持使用 null
作为您的默认值键,您也可以将 string
更改为可为 null 的字符串 string?
。
我最近在将分支逻辑简化为 switch 表达式和利用模式匹配方面发现了很多价值。特别是在我有多个数据元素需要考虑的情况下。
return tuple switch
{
("Foo", "Bar") => "FooBar",
("Foo", _ ) => "Foo",
(_, "Bar") => "Bar",
(_, _) => "Default Value"
};
我对“元组”变量的任何给定值的预期行为如下:
var tuple = ("Hello", "World") -> "Default Value"
var tuple = ("Foo", "World") -> "Foo"
var tuple = ("Foo", "") -> "Foo"
var tuple = ("Foo", null) -> "Foo"
var tuple = ("Hello", "Bar") -> "Bar"
这一切都很好。
我最近发现了一种情况,我想要的是必须按照“最具体到最不具体”的顺序检查的一系列规则,如果值不存在,则使用合理的默认值。所以实际上与上面的模式匹配序列相同。但是,我需要我的最终用户能够自己配置模式,并且模式案例是动态的(即来自数据库 table)。
所以,给定这些数据记录:
Input1, Input2, ReturnValue
"Foo", "Bar", "FooBar"
"Foo", NULL, "Foo"
NULL, "Bar", "Bar"
NULL, NULL, "Default Value"
我想“生成”这些案例,就像我上面的硬编码示例一样。
("Foo", "Bar") => "FooBar",
("Foo", _ ) => "Foo",
(_, "Bar") => "Bar",
(_, _) => "Default Value"
然后,如果用户想要添加新的“规则”,他们会添加一条新记录
Input1, Input2, ReturnValue
"Hello","World", "I'm the super special return value".
这将创建以下模式案例:
("Hello", "World") => "I'm the super special return value",
计算时对应的结果为:
var tuple = ("Hello", "World") -> "I'm the super special return value"
var tuple = ("Hello", "Other") -> "Default Value"
在我看来,我想做一些事情:
var switchOptions = dataRecords.Select(record =>
{
var pattern = (record.Input1 ?? '_', record.Input2 ?? '_');
var func = (pattern) => record.Result;
return func;
});
//and then somehow build the switch expression out of these options.
为什么这不起作用有几个原因是有道理的,我肯定不限于:
- switch 表达式语法不是具有 AddPattern() 的对象。
- 表达式中的字符“_”和 _ 运算符不是一回事...
我想到的另一个选择是将记录集映射到字典中,其中 Key 是元组(列:Input1、Input2),值是预期的 return 值(列:返回值)。这个问题是,它没有提供任何能力通过简单的键查找将 NULL 数据库值视为丢弃模式。
归根结底,我的问题是: 我假设 switch 表达式语法只是对幕后更复杂实现的一些很好的糖分覆盖。我可以使用 C# 9 中已有的实现来实现“动态”开关表达式的想法吗?还是我找错了树,需要自己完全实现?
我不确定您是否在寻找某种 code-generation,但是通过一些 Linq 聚合,您可以将 patterns/records 放在一个序列中并将结果用作有点像 switch-expression 模式匹配的功能。重要的是 dataRecords
包含您希望它们被评估的顺序的记录:
public record Record(string Input1, string Input2, string ReturnValue);
public record Pattern(Func<string, string, bool> Predicate, string ReturnValue);
Pattern CreatePattern(Record rec)
{
return new (
(l, r) =>
(l == rec.Input1 || rec.Input1 == null)
&& (r == rec.Input2 || rec.Input2 == null),
rec.ReturnValue
);
}
// Create your pattern matching "switch-expression"
var switchExp = dataRecords
.Reverse()
.Select(CreatePattern)
.Aggregate<Pattern, Func<string, string, string>>(
(_, _) => null,
(next, pattern) =>
(l, r) => pattern.Predicate(l, r) ? pattern.ReturnValue : next(l, r)
);
switchExp("abc", "bar"); // "bar"
看到了in action.
您必须自己实施。 switch 模式匹配类似于常规使用的 switch case,需要编译时常量,并且很可能用跳转 table 实现。因此不能在运行时修改。
感觉您要实现的目标应该不会太难。像这样
PatternDict = new Dictionary<string, Dictionary<string, string>>();
PatternDict["_"] = new Dictionary<string, string>();
PatternDict["_"]["_"] = null;
更新码为:
Dictionary<string, string> dict;
if (!PatternDict.TryGetValue(input1, out dict)) {
dict = new Dictionary<string, string>();
dict["_"] = "default";
}
dict[input2] = returnValue;
PatternDict[input1] = dict;
检索码为:
Dictionary<string, string> dict;
if (!PatternDict.TryGetValue(input1, out dict)) {
dict = PatternDict["_"];
}
string returnVal;
if (!dict.TryGetValue(input2, out returnVal)) {
returnVal = dict["_"];
}
return returnVal;
如果您使用的新版本的 C# 支持使用 null
作为您的默认值键,您也可以将 string
更改为可为 null 的字符串 string?
。