最近的C#相当于F#匹配表达式?

Gre*_*ech 7 c# f# casting guard-clause

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

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...
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,如果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
Run Code Online (Sandbox Code Playgroud)

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

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

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

// etc.

return false;
Run Code Online (Sandbox Code Playgroud)

我可以使用as关键字来避免测试/转换模式更快,但更加冗长.

在C#中有没有优雅的方法呢?

Bri*_*ian 11

您可以在'o'和helper类上创建一个扩展方法来启用编程模型

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

但要注意在这里做太多类似DSL的hackery,以免你最终得到一个只有你理解的API.

也可以看看

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

  • 并将扩展方法放在自己的命名空间中,否则人们会抱怨你已经为所有类型的intellisense列表添加了两个方法. (2认同)

Jul*_*iet 9

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

if (o is {DataType})
{
    {Property} = ({DataType})o;
    return true;
}
Run Code Online (Sandbox Code Playgroud)

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

    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;
    }
Run Code Online (Sandbox Code Playgroud)

  • 虽然Brians方法更聪明,但这是我在C#中实现它的方式.非常干净和直截了当.F#方法在F#中看起来很棒,但在C#中却没有那么多. (2认同)

Gre*_*ech 8

我一直在玩一个小小的匹配器(灵感来自Brian的答案),它允许类型检查,保护条款,以及从整个事物中返回结果.它使用类型推断,因此您需要指定类型的唯一位置是您真正想要的位置.

因此,想象类型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);
Run Code Online (Sandbox Code Playgroud)

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

代码非常冗长,因为它在后台需要一些部分指定的构建器类来允许类型推断工作,所以我不能在这里发布它.但是,如果有人感兴趣,请在评论中告诉我,我会将其粘贴在我的博客上,并在此处添加链接.