开关/模式匹配的想法

我最近一直在研究F#,尽管我不太可能马上跳过栅栏,但它肯定会突出显示C#(或库支持)可以使生活更轻松的一些领域。

特别是,我正在考虑F#的模式匹配功能,它允许使用非常丰富的语法 - 比当前的开关/条件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 <Vehicle,int>。

    [注意 - 也许切换/案例这里是错误的条款...但它显示了这个想法]

    对我来说,这比使用重复的if / else或复合三元条件(对于非平凡表达式非常混乱 - 括号内的丰富)等价得多。 它还避免了大量的投射,并且允许简单的扩展(直接或通过扩展方法)进行更具体的匹配,例如InRange(...)匹配可与VB Select ... Case“x to y “用法。

    我只是想衡量一下,如果人们认为像上面这样的构造有很多好处(在没有语言支持的情况下)?

    另外请注意,我一直在玩3种以上的变体:

  • 用于评估的Func <TSource,TValue>版本 - 与组合三元条件语句相当
  • Action <TSource>版本 - 与if / else if / else if / else if / else相当
  • 一个Expression <Func <TSource,TValue >>版本 - 作为第一个,但可供任意LINQ提供者使用
  • 此外,使用基于表达式的版本可以实现表达式树重写,基本上将所有分支内联到单个复合条件表达式中,而不是使用重复的调用。 我最近没有检查,但在一些早期的Entity Framework构建中,我似乎回想起这是必要的,因为它不太喜欢InvocationExpression。 它还允许更有效地使用LINQ到对象,因为它避免了重复的委托调用 - 测试显示与上面(使用表达式表单)相同的速度执行相同的速度[事实上比同等速度的C#复合条件语句。 为了完整性,基于Func <...>的版本花了C#条件语句的4倍,但仍然非常快,并且在大多数使用情况下不太可能成为主要瓶颈。

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


    我知道这是一个老话题,但在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));
    }
    

    Bart De Smet的优秀博客包含8部分内容,涉及您所描述的内容。 在这里找到第一部分。


    在尝试在C#中完成这些“功能”的事情(甚至尝试写一本书)之后,我得出的结论是,除了少数例外,否则这样的事情不会有太大的帮助。

    主要原因是像F#这样的语言从真正支持这些功能中获得了很大的力量。 不是“你可以做到”,而是“很简单,很明显,这是预期的”。

    例如,在模式匹配中,您可以让编译器告诉您是否存在不完整的匹配,或者其他匹配不会被命中。 这对于开放式类型来说不太有用,但是当匹配歧视的联合或元组时,它非常漂亮。 在F#中,你期望人们模式匹配,并且它立即有意义。

    “问题”在于,一旦开始使用某些功能概念,想要继续就很自然。 但是,在C#中利用元组,函数,部分方法应用程序和currying,模式匹配,嵌套函数,泛型,monad支持等非常快,非常难看。 这很有趣,一些非常聪明的人在C#中做了一些非常酷的事情,但实际上使用它感觉很沉重。

    我最终在C#中经常使用(跨项目):

  • 序列函数,通过IEnumerable的扩展方法。 像ForEach或Process(“应用”?) - 对列举的序列项执行操作)适合使用,因为C#语法支持它。
  • 抽象通用语句模式。 复杂的try / catch / finally块或其他涉及的(通常是泛型的)代码块。 扩展LINQ到SQL也适用于这里。
  • 元组,在一定程度上。
  • **但请注意:缺少自动泛化和类型推断确实会阻碍使用这些功能。 **

    所有这些都像其他人提到的那样,在一个小团队中,为了特定的目的,是的,如果你被C#困住了,也许他们可以提供帮助。 但根据我的经验,他们通常觉得比他们的价值更麻烦 - YMMV。

    其他一些链接:

  • Mono.Rocks游乐场有许多类似的东西(以及非功能性编程但是有用的附加)。
  • Luca Bolognese的功能性C#库
  • Matthew Podwysocki在MSDN上的功能性C#
  • 链接地址: http://www.djcxy.com/p/14761.html

    上一篇: switch / pattern matching idea

    下一篇: Can I declare variables inside an Objective