与 F# 匹配表达式最接近的 C# 等效项?

发布于 2024-08-07 02:25:14 字数 897 浏览 7 评论 0原文

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

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

I'm in the situation where a lot of my classes are containers of well-known but unordered objects of different types, e.g. a container may look as follows:

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...
    }
}

So if o is of type A it should be stored in the A property, type B in the B property and so on.

In F# the StoreIfKnown method could be written something like the following (excuse the syntax errors, my F# is not great and quite rusty):

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

But in C# the only way seems to be the rather verbose:

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

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

// etc.

return false;

I could do it with the as keyword to avoid the test/cast pattern which would be faster, but is even more verbose.

Is there any elegant way to do this in C#?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

爱的十字路口 2024-08-14 02:25:14

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

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

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

另请参见

You could author an extension method on 'o' and helper classes to enable a programming model like

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

But be wary of doing too much DSL-like hackery here, lest you end up with an API only you understand.

See also

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

匿名。 2024-08-14 02:25:14

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

Its not as nifty as Brian's solution, but this doesn't require defining a new DSL. You'll notice your repeating the following code:

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

Its easy enough to pull that template into its own method, resulting in something like this:

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;
    }
}

If you're working with reference types, you can take advantage of type inference with the following adjustments:

    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;
    }
止于盛夏 2024-08-14 02:25:14

我一直在玩一个小匹配构建器(灵感来自 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);

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

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

I've been playing around with a little match builder (inspired by Brian's answer) which allows type checking, guard clauses, and returning of a result from the whole thing. It uses type inference so the only place you need to specify a type is where you actually want to.

So, imagining type C has an IsActive property which we want to be true, it would look something like this:

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);

Which I think is pretty readable, especially as it allows a predicate to be run against the derived type before actually matching which is something I do need.

The code is quite lengthy as it needs a number of partially-specified builder classes in the background to allow the type inference to work, so I can't really post it here. But if anyone's interested let me know in the comments and I'll stick it up on my blog and put a link here.

失而复得 2024-08-14 02:25:14

Bart de Smet 曾经对模式匹配感到疯狂,从 此处(一直到第 8 部分)。如果您设法读完所有这些内容,那么 C# 中的模式匹配就不会有任何问题了。如果有的话,stackoverflow 可能无法回答:)

Bart de Smet once went crazy with pattern matching, starting here (goes all the way up to part 8). If you ever manage to get through all this content there shouldn't be any questions left to pattern matching in C#. If there are, they probably cannot be answered by stackoverflow :)

笑忘罢 2024-08-14 02:25:14

截至 2016 年 8 月和 C# 7.0 预览版,对模式匹配的支持有限。您可以尝试使用 Visual Studio “15”预览 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;
        }
    }
}

As of August 2016 and preview of C# 7.0, there is a limited support for pattern matching. You can try if by using Visual Studio “15” Preview 4.

According to the MSDN blog, you can use patterns in two places:

  • on the right-hand side of is expressions

  • in the case clauses in switch statements

Possible patterns are:

  • Constant patterns of the form c (where c is a constant expression in C#), which test that the input is equal to c

  • Type patterns of the form T x (where T is a type and x is an identifier), which test that the input has type T, and if so, extracts the value of the input into a fresh variable x of type T

  • Var patterns of the form var x (where x is an identifier), which always match, and simply put the value of the input into a fresh variable x with the same type as the input

I didn't install Visual Studio 15, so I'm not sure I rewrote your code correctly, but it should not be far off:

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;
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文