一尘不染

开关/模式匹配的想法

c#

我最近一直在研究F#,虽然我不太可能很快就克服障碍,但它无疑突出了C#(或库支持)可以使生活更轻松的某些领域。

特别是,我正在考虑F#的模式匹配功能,该功能允许使用非常丰富的语法-比当前的switch / conditional
C#等效项更具表现力。我不会尝试举一个直接的例子(我的F#不符合要求),但总而言之,它允许:

  • 按类型匹配(对有区别的联合进行全覆盖检查)[请注意,这还会推断出绑定变量的类型,为成员提供访问权限等]
  • 谓词匹配
  • 上面的组合(可能还有我不知道的其他情况)

虽然C#最终会借用其中的一些功能是很可爱的,但是在此期间,我一直在研究可以在运行时完成的工作-例如,将某些对象组合在一起以允许以下操作相当容易:

var getRentPrice = new Switch<Vehicle, int>()
        .Case<Motorcycle>(bike => 100 + bike.Cylinders * 10) // "bike" here is typed as Motorcycle
        .Case<Bicycle>(30) // returns a constant
        .Case<Car>(car => car.EngineType == EngineType.Diesel, car => 220 + car.Doors * 20)
        .Case<Car>(car => car.EngineType == EngineType.Gasoline, car => 200 + car.Doors * 20)
        .ElseThrow(); // or could use a Default(...) terminator

其中getRentPrice是Func

[注意-也许这里的Switch / Case是错误的术语…但是它表明了这个想法]

对我来说,这比使用重复的if / else或复合三元条件(对于非平凡的表达式会变得非常混乱-方括号)更加清楚。它还避免了 很多
转换,并允许简单地扩展(直接或通过扩展方法)到更特定的匹配项,例如,与VB Select … Case相当的InRange(…)匹配项。 ”的用法。

我只是在尝试衡量人们是否认为上述结构(在没有语言支持的情况下)有很多好处?

另外请注意,我一直在使用上述3种变体:

  • 用于评估的Func 版本-与复合三元条件语句相当
  • 一个Action 版本-与if / else if / else if / else if / else相当
  • 一个Expression >版本-作为第一个版本,但可由任意LINQ提供程序使用

此外,使用基于表达式的版本可以重写表达式树,实质上是将所有分支内联到单个复合条件表达式中,而不是使用重复调用。我最近没有检查过,但是在某些早期的Entity
Framework构建中,我似乎想起来这是必要的,因为它不太喜欢InvocationExpression。由于它避免了重复的委托调用,因此还可以更有效地使用LINQ-
to-Objects-
测试显示,与上述C#相比,上述匹配(使用Expression形式)以相同的速度[实际上更快]进行了匹配。复合条件语句。为了完整起见,基于Func
<…>的版本花费的时间是C#条件语句的4倍,但仍非常快,并且在大多数用例中不太可能成为主要瓶颈。

我欢迎上面的任何想法/输入/批评/等等(或关于更丰富的C#语言支持的可能性…这里希望;-p)。


阅读 314

收藏
2020-05-19

共1个答案

一尘不染

我知道这是一个老话题,但是在c#7中,您可以执行以下操作:

switch(shape)
{
    case Circle c:
        WriteLine($"circle with radius {c.Radius}");
        break;
    case Rectangle s when (s.Length == s.Height):
        WriteLine($"{s.Length} x {s.Height} square");
        break;
    case Rectangle r:
        WriteLine($"{r.Length} x {r.Height} rectangle");
        break;
    default:
        WriteLine("<unknown shape>");
        break;
    case null:
        throw new ArgumentNullException(nameof(shape));
}
2020-05-19