[C#]Expression用法

Expression Tree

前言

從C#3.0開始可以使用 Expression trees 來取得 object的 type, members等等...的功能。

那這個跟func有什麼差別呢?  在這邊舉個例子

static void Main(string[] args)
{            
    string result = MyFunc(x => x.Name);
    Console.WriteLine(result); //Print : Shoes
}

private static string MyFunc(Func<Product, object> func)
{
    var product = new Product()
    {
        Id = 1,
        Name = "Shoes",
        Price = 1000
    };

    return func(product).ToString();
}

這個簡單的例子,可以指定我要取得Name 這個Property 的值。  在這邊我們只能知道他是傳哪一個Property,那如果我要取得Property名稱呢?  單純用Func是沒辦法做到的,要另外加上Expression的用法。

static void Main(string[] args)
{            
    string result = MyFunc(x => x.Name);
    Console.WriteLine(result); //Print : Name
}

private static string MyFunc(Expression<Func<Product, object>> e)
{
    Expression expression = e.Body;

    if (expression is MemberExpression)
    {
        var member = expression as MemberExpression;
        return member.Member.Name;
    }

    return string.Empty;
}

只要這樣寫,就可以取得Property Name了,那如果要在Expression取得值得話,只要把Expression compile成Func就可以了,範例如下:

static void Main(string[] args)
{            
    string result = MyFunc(x => x.Name);
    Console.WriteLine(result); //Print : Name = Shoes
}

private static string MyFunc(Expression<Func<Product, object>> e)
{
    Expression expression = e.Body;

    if (expression is MemberExpression)
    {
        var product = new Product()
        {
            Id = 1,
            Name = "Shoes",
            Price = 1000
        };

        var member = expression as MemberExpression;

        //Compile成func
        Func<Product, object> func = e.Compile();

        var value = func(product);

        return $"{member.Member.Name} = {value}";
    }

    return string.Empty;
}

 

Expression 根據不同的存取方式會有不同的NodeType 例如 MyFunc(x => "test")遇到這種情形,就要去判斷NodeType來決定後面要怎麼處理,範例如下:

static void Main(string[] args)
{            
    string result = MyFunc(x => "test");
    Console.WriteLine(result); //Print : test
}

private static string MyFunc(Expression<Func<Product, object>> e)
{
    Expression expression = e.Body;

    if (expression.NodeType == ExpressionType.Constant)
    {
        var constant = expression as ConstantExpression;
        return constant.Value.ToString();                
    }
    
    return string.Empty;
}

最後介紹今天要介紹的另一種,也是很重要的一種NodeType,ExpressionType.Convert。當我們傳入MyFunc(x => x.Price)的時候,因為Price是value type decimal 所以會boxing(轉型)成object,因為有轉型的動作,所以做法上跟MyFunc(x => x.Name)不太一樣,範例如下:

static void Main(string[] args)
{            
    string result = MyFunc(x => x.Price);
    Console.WriteLine(result); //Print : Price
}
private static string MyFunc(Expression<Func<Product, object>> e)
{
    Expression expression = e.Body;

    if (expression.NodeType == ExpressionType.Convert)
    {
        var unary = expression as UnaryExpression;

        var member = unary.Operand as MemberExpression;

        return member.Member.Name;
    }
    
    return string.Empty;
}

 

小結

Expression Tree基本用法,大致上就是這樣,至於能用在什麼地方,我想最常用的地方應該就是寫自訂的Html Helper了吧。

 

參考:

https://blogs.msdn.microsoft.com/csharpfaq/2010/03/11/how-can-i-get-objects-and-property-values-from-expression-trees/

https://blogs.msdn.microsoft.com/csharpfaq/2010/01/06/getting-information-about-objects-types-and-members-with-expression-trees/

http://stackoverflow.com/questions/12975373/expression-for-type-members-results-in-different-expressions-memberexpression

 

 

一天一分享,身體好健康。

該追究的不是過去的原因,而是現在的目的。