Implementing pattern matching in C#

后端 未结 5 1737
耶瑟儿~
耶瑟儿~ 2021-02-04 03:54

In Scala, you can use pattern matching to produce a result depending on the type of the input. For instance:

val title = content match {
    case blogPost: BlogP         


        
相关标签:
5条回答
  • 2021-02-04 04:17

    Using Functional C# (from @Alireza)

    var title = content.Match()
       .With<BlogPost>(blogPost => blogPost.Blog.Title + ": " + blogPost.Title)
       .With<Blog>(blog => blog.Title)
       .Result<string>();
    
    0 讨论(0)
  • 2021-02-04 04:24

    In order to ensure total pattern matching, you would need to build the function into the type itself. Here's how I'd do it:

    public abstract class Content
    {
        private Content() { }
    
        public abstract T Match<T>(Func<Blog, T> convertBlog, Func<BlogPost, T> convertPost);
    
        public class Blog : Content
        {
            public Blog(string title)
            {
                Title = title;
            }
            public string Title { get; private set; }
    
            public override T Match<T>(Func<Blog, T> convertBlog, Func<BlogPost, T> convertPost)
            {
                return convertBlog(this);
            }
        }
    
        public class BlogPost : Content
        {
            public BlogPost(string title, Blog blog)
            {
                Title = title;
                Blog = blog;
            }
            public string Title { get; private set; }
            public Blog Blog { get; private set; }
    
            public override T Match<T>(Func<Blog, T> convertBlog, Func<BlogPost, T> convertPost)
            {
                return convertPost(this);
            }
        }
    
    }
    
    public static class Example
    {
        public static string GetTitle(Content content)
        {
            return content.Match(blog => blog.Title, post => post.Blog.Title + ": " + post.Title);
        }
    }
    
    0 讨论(0)
  • 2021-02-04 04:26

    Pattern matching is one of those lovely features mostly found in functional programming languages like F#. There is a great project going on in codeplex named Functional C#. Consider the following F# code:

    let operator x = match x with
                     | ExpressionType.Add -> "+"
    
    let rec toString exp = match exp with
                           | LambdaExpression(args, body) -> toString(body)
                           | ParameterExpression(name) -> name
                           | BinaryExpression(op,l,r) -> sprintf "%s %s %s" (toString l) (operator op) (toString r)
    

    Using the Functional C# library, the C# equivalent would be:

    var Op = new Dictionary<ExpressionType, string> { { ExpressionType.Add, "+" } };
    
    Expression<Func<int,int,int>> add = (x,y) => x + y;
    
    Func<Expression, string> toString = null;
     toString = exp =>
     exp.Match()
        .With<LambdaExpression>(l => toString(l.Body))
        .With<ParameterExpression>(p => p.Name)
        .With<BinaryExpression>(b => String.Format("{0} {1} {2}", toString(b.Left), Op[b.NodeType], toString(b.Right)))
        .Return<string>();
    
    0 讨论(0)
  • 2021-02-04 04:30

    Generic implementation I'm using, that can match against the type, condition or a value:

    public static class Match
    {
        public static PatternMatch<T, R> With<T, R>(T value)
        {
            return new PatternMatch<T, R>(value);
        }
    
        public struct PatternMatch<T, R>
        {
            private readonly T _value;
            private R _result;
    
            private bool _matched;
    
            public PatternMatch(T value)
            {
                _value = value;
                _matched = false;
                _result = default(R);
            }
    
            public PatternMatch<T, R> When(Func<T, bool> condition, Func<R> action)
            {
                if (!_matched && condition(_value))
                {
                    _result = action();
                    _matched = true;
                }
    
                return this;
            }
    
            public PatternMatch<T, R> When<C>(Func<C, R> action)
            {
                if (!_matched && _value is C)
                {
                    _result = action((C)(object)_value);
                    _matched = true;
                }
                return this;
            }
    
    
            public PatternMatch<T, R> When<C>(C value, Func<R> action)
            {
                if (!_matched && value.Equals(_value))
                {
                    _result = action();
                    _matched = true;
                }
                return this;
            }
    
    
            public R Result => _result;
    
            public R Default(Func<R> action)
            {
                return !_matched ? action() : _result;
            }
        }
    }
    

    And in your case, usage would look like:

    Match.With<IContent, string>(content)
         .When<BlogPost>(blogPost => blogPost.Blog.Title)
         .When<Blog>(blog => blog.Title)
         .Result; // or just .Default(()=> "none");
    

    Some other examples:

    var result = Match.With<IFoo, int>(new Foo() { A = 5 })
        .When<IFoo>(foo => foo.A)
        .When<IBar>(bar => bar.B)
        .When<string>(Convert.ToInt32)
        .Result;
    Assert.Equal(5, result);
    
    var result = Match.With<int, string>(n)
        .When(x => x > 100, () => "n>100")
        .When(x => x > 10, () => "n>10")
        .Default(() => "");
    Assert.Equal("n>10", result);
    
     var result = Match.With<int, string>(5)
         .When(1, () => "1")
         .When(5, () => "5")
         .Default(() => "e");
     Assert.Equal("5", result);
    
    0 讨论(0)
  • 2021-02-04 04:33

    Check out my pattern matching implementation: repo

    It's based on expressions, so it offers equal perfomance with nested ifs.

    Example usage:

    string s1 = "Hello";
    string s2 = null;
    
    Func<Option<string>> match = new Matcher<Option<string>>
    {
         {s => s is None, s => Console.WriteLine("None")},
         {s => s is Some, s => Console.WriteLine((string)s) // or s.Value
    };
    
    match(s1); // Hello
    match(s2); // None
    

    Available throught NuGet: Nuget package

    0 讨论(0)
提交回复
热议问题