【问题标题】:Closest C# equivalent to the F# match expression?与 F# 匹配表达式最接近的 C#?
【发布时间】:2010-12-03 07:26:25
【问题描述】:

我的很多类都是不同类型的知名但无序对象的容器,例如容器可能如下所示:

public class Container
{
    public A A { get; private set; }
    public B B { get; private set; }
    public C C { get; private set; }

    public bool StoreIfKnown(object o)
    {
        // TODO...
    }
}

因此,如果o 的类型为A,则应将其存储在A 属性中,在B 属性中键入B,依此类推。

在 F# 中,StoreIfKnown 方法可以编写如下(请原谅语法错误,我的 F# 不是很好而且很生锈):

match o with
| ?: A a -> A <- a; true
| ?: B b -> B <- b; true
| ?: C c -> C <- c; true
| _ -> false

但在 C# 中,唯一的方法似乎是相当冗长:

if (o is A)
{
    this.A = (A)o;
    return true;
}

if (o is B)
{
    this.B = (B)o;
    return true;
}

// etc.

return false;

我可以使用 as 关键字来避免更快但更冗长的测试/强制转换模式。

在 C# 中有什么优雅的方法可以做到这一点吗?

【问题讨论】:

  • 我很震惊居然有人在使用 F#...
  • @pixelbobby:请注意,有 478 个 F# 问题,而 Haskell 有 409 个,Scala 有 382 个,Lisp 有 353 个,Erlang 有 305 个,OCaml 有 96 个。 F# 看起来是 SO 上最流行的函数式编程语言,这可能是由于 hubFS 社区和想要跳上函数式编程火车的 .NET 开发人员的交叉。
  • 我不会说我使用 F#,我只是想了解它的功能。虽然我很想有一份工作,它是我使用的主要语言(假设适合目的)来提高我的技能!至少了解一些使用与您通常使用的语言不同的范例的语言的基础知识可以提高您的全面编程能力。
  • @pixelbobby:F# 已经被 Microsoft 用于 XBox 分析和许多金融集团的生产环境中!
  • @pixelbobby:我几乎所有的新代码都是用 F# 编写的。几个月后,我现在在 F# 中更快了。它只是一种更高效的数据处理语言。根据您编写的代码,您的里程可能会有所不同。甚至我的 C# 代码看起来也很实用。我可以确认,正如 stringer 指出的那样,许多金融集团在生产中使用它。

标签: c# casting f# guard-clause


【解决方案1】:

您可以在“o”和辅助类上编写扩展方法,以启用类似的编程模型

o.Match<A>( a => { this.A = a; return true; } )
 .Match<B>( b => { this.B = b; return true; } )
 .Else( () => { return false; } )

但要小心不要在这里做太多类似 DSL 的骇客,以免你最终得到一个只有你理解的 API。

另见

http://blogs.msdn.com/lucabol/archive/2008/07/15/a-c-library-to-write-functional-code-part-v-the-match-operator.aspx

【讨论】:

【解决方案2】:

它不像 Brian 的解决方案那样漂亮,但这不需要定义新的 DSL。您会注意到您重复了以下代码:

if (o is {DataType})
{
    {Property} = ({DataType})o;
    return true;
}

很容易将该模板拉入自己的方法中,结果如下:

public class Container
{
    public A A { get; private set; }
    public B B { get; private set; }
    public C C { get; private set; }

    private bool TestProp<T>(object o, Action<T> f)
    {
        if (o is T)
            return false;

        f((T)o);
        return true;
    }

    public bool StoreIfKnown(object o)
    {
        return
            TestProp<A>(o, x => A = x) ||
            TestProp<B>(o, x => B = x) ||
            TestProp<C>(o, x => C = x) ||
            false;
    }
}

如果您正在使用引用类型,则可以通过以下调整来利用类型推断:

    private bool TestProp<T>(T o, Action<T> f)
    {
        if (o == null)
            return false;

        f(o);
        return true;
    }

    public bool StoreIfKnown(object o)
    {
        return
            TestProp(o as A, x => A = x) ||
            TestProp(o as B, x => B = x) ||
            TestProp(o as C, x => C = x) ||
            false;
    }

【讨论】:

  • +1 - 这实际上与我目前所得到的非常相似(尽管我不想用这么多细节来混淆这个问题)。它工作得很好,但感觉有点笨拙。
  • 虽然 Brians 的方法更聪明,但这是我在 C# 中实现它的方式。非常干净和直接。 F# 方法在 F# 中看起来很棒,但在 C# 中则不然。
  • 请注意,您可以跳过最后的false 值,因为a||false 等价于aa||b||false 等价于a||b,等等。
【解决方案3】:

我一直在玩一个小的匹配生成器(受Brian's answer 启发),它允许类型检查、保护子句和从整个事件中返回结果。它使用类型推断,因此您唯一需要指定类型的地方就是您真正想要的地方。

所以,假设C 有一个IsActive 属性,我们希望它是true,它看起来像这样:

var stored = Match.Against(o)
    .When<A>().Then(a => { this.A = a; return true; })
    .When<B>().Then(b => { this.B = b; return true; })
    .When<C>(c => c.IsActive).Then(c => { this.C = c; return true; })
    .Otherwise(a => false);

我认为这非常易读,特别是因为它允许在实际匹配之前针对派生类型运行谓词,这是我确实需要的。

代码很长,因为它需要在后台使用一些部分指定的构建器类来允许类型推断工作,所以我不能在这里发布它。但如果有人感兴趣,请在 cmets 中告诉我,我会将其贴在我的博客上并在此处放一个链接。

【讨论】:

    【解决方案4】:

    Bart de Smet 曾经在模式匹配方面疯狂,从 here 开始(一直到第 8 部分)。如果您设法了解所有这些内容,那么在 C# 中的模式匹配应该不会有任何问题。如果有,stackoverflow 可能无法回答它们:)

    【讨论】:

    • 看起来很有趣,我会通读一遍。虽然不幸的是,他的网站似乎刚刚暂时中断:-S
    【解决方案5】:

    截至 2016 年 8 月和 C# 7.0 预览版,there is a limited support for pattern matching。您可以尝试使用Visual Studio “15” Preview 4

    根据 MSDN 博客,您可以在两个地方使用模式:

    • 右边是表达式

    • switch 语句中的 case 子句

    可能的模式是:

    • c 形式的常量模式(其中 c 是 C# 中的常量表达式),用于测试输入是否等于 c

    • T x 形式的类型模式(其中 T 是类型,x 是标识符),它测试输入是否具有类型 T,如果是,则将输入的值提取到新变量 x 中T型

    • var x 形式的变量模式(其中 x 是一个标识符),它们始终匹配,并且只需将输入的值放入与输入具有相同类型的新变量 x 中

    我没有安装 Visual Studio 15,所以我不确定我是否正确地重写了您的代码,但应该不会太远:

    public class Container
    {
        public A A { get; private set; }
        public B B { get; private set; }
        public C C { get; private set; }
    
        public bool StoreIfKnown(object obj)
        {
            switch (obj)
            {
                case A a:
                    this.A = a
                    // I don't put "break" because I'm returning value from a method
                    return true;
                case B b:
                    this.B = b
                    return true;
                case C c:
                    this.C = c
                    return true;
                default:
                    WriteLine("<other>");
                    return false;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-09
      相关资源
      最近更新 更多