繁体   English   中英

最接近C#的F#匹配表达式吗?

[英]Closest C# equivalent to the F# match expression?

我遇到的情况是,我的许多类都是不同类型的知名但无序对象的容器,例如,一个容器可能如下所示:

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属性,类型BB属性等。

在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#中有什么优雅的方法可以做到这一点吗?

您可以在'o'和helper类上编写扩展方法,以启用诸如

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/ac-library-to-write-functional-code-part-v-the-match-operator.aspx

它不像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;
    }

我一直在玩一个小的match生成器(由Brian的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);

我认为这很容易理解,尤其是因为它允许谓词在实际匹配之前针对派生类型运行,这是我确实需要的。

该代码很长,因为它需要在后台使用多个部分指定的构建器类来允许类型推断起作用,因此我无法在此处进行实际发布。 但是,如果有人感兴趣,请在评论中让我知道,我将其保留在我的博客上并在此处添加链接。

Bart de Smet曾经疯狂地进行模式匹配,从这里开始(一直到第8部分)。 如果您设法遍历所有这些内容,那么C#中的模式匹配就不会有任何问题了。 如果有,它们可能无法通过stackoverflow来回答:)

从2016年8月开始,在C#7.0的预览版中, 对模式匹配的支持有限 您可以尝试使用Visual Studio“ 15”预览版4

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

  • 表达式的右侧

  • switch语句中的case子句

可能的模式是:

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

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

  • 形式始终为var x(其中x是标识符)的var模式,只需将输入的值放入与输入具有相同类型的新鲜变量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;
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM