What fluent interfaces have you made or seen in C# that were very valuable? What was so great about them?

后端 未结 11 1192
-上瘾入骨i
-上瘾入骨i 2021-01-31 05:42

\"Fluent interfaces\" is a fairly hot topic these days. C# 3.0 has some nice features (particularly extension methods) that help you make them.

FYI, a fluent API means

11条回答
  •  既然无缘
    2021-01-31 05:52

    Here's one I made just yesterday. Further thought may lead me to change the approach, but even if so, the "fluent" approach let me accomplish something I otherwise could not have.

    First, some background. I recently learned (here on StackOverflow) a way to pass a value to a method such that the method would be able to determine both the name and the value. For example, one common use is for parameter validation. For example:

    public void SomeMethod(Invoice lastMonthsInvoice)
    {
         Helper.MustNotBeNull( ()=> lastMonthsInvoice);
    }
    

    Note there's no string containing "lastMonthsInvoice", which is good because strings suck for refactoring. However, the error message can say something like "The parameter 'lastMonthsInvoice' must not be null." Here's the post that explains why this works and points to the guy's blog post.

    But that is just background. I'm using the same concept, but in a different way. I am writing some unit tests, and I want to dump certain property values out to the console so they show up in the unit test output. I got tired of writing this:

    Console.WriteLine("The property 'lastMonthsInvoice' has the value: " + lastMonthsInvoice.ToString());
    

    ... because I have to name the property as a string and then refer to it. So I made it where I could type this:

    ConsoleHelper.WriteProperty( ()=> lastMonthsInvoice );
    

    And get this output:

    Property [lastMonthsInvoice] is: 

    produces>

    Now, here's where a fluent approach allowed me to do something I otherwise couldn't do.

    I wanted to make ConsoleHelper.WriteProperty take a params array, so it could dump many such property values to the console. To do that, its signature would look like this:

    public static void WriteProperty(params Expression>[] expr)
    

    So I could do this:

    ConsoleHelper.WriteProperty( ()=> lastMonthsInvoice, ()=> firstName, ()=> lastName );
    

    However, that doesn't work due to type inference. In other words, all of these expressions do not return the same type. lastMonthsInvoice is an Invoice. firstName and lastName are strings. They cannot be used in the same call to WriteProperty, because T is not the same across all of them.

    This is where the fluent approach came to the rescue. I made WriteProperty() return something. The type it returned is something I can call And() on. This gives me this syntax:

    ConsoleHelper.WriteProperty( ()=> lastMonthsInvoice)
         .And( ()=> firstName)
         .And( ()=> lastName);
    

    This is a case where the fluent approach allowed something that otherwise would not have been possible (or at least not convenient).

    Here's the full implementation. As I said, I wrote it yesterday. You'll probably see room for improvement or maybe even better approaches. I welcome that.

    public static class ConsoleHelper
    {
        // code where idea came from ...
        //public static void IsNotNull(Expression> expr)
        //{
        // // expression value != default of T
        // if (!expr.Compile()().Equals(default(T)))
        // return;
    
        // var param = (MemberExpression)expr.Body;
        // throw new ArgumentNullException(param.Member.Name);
        //}
    
        public static PropertyWriter WriteProperty(Expression> expr)
        {
            var param = (MemberExpression)expr.Body;
            Console.WriteLine("Property [" + param.Member.Name + "] = " + expr.Compile()());
            return null;
        }
    
        public static PropertyWriter And(this PropertyWriter ignored, Expression> expr)
        {
            ConsoleHelper.WriteProperty(expr);
            return null;
        }
    
        public static void Blank(this PropertyWriter ignored)
        {
            Console.WriteLine();
        }
    }
    
    public class PropertyWriter
    {
        /// 
        /// It is not even possible to instantiate this class. It exists solely for hanging extension methods off.
        /// 
        private PropertyWriter() { }
    }
    

提交回复
热议问题