Using C# LINQ Expressions for both Value Types and Reference Types

社会主义新天地 提交于 2019-12-10 18:49:45

问题


I'm using MVC for REST so that I can take advantage of Razor for outputting different types. CSV is one of those outputs. Instead of writing this template for each input type:

ID,Created,Content
@foreach (var item in Model.TimeData)
{
<text>@item.ID,@item.Created,"@Html.Raw(item.Content.Replace("\"", "\"\""))"</text>
}

I wanted to make use of params and System.Linq.Expressions.Expression to write something like this:

@{
    Html.WriteCsv<TimeObject>(Model.TimeData, p => p.ID, p => p.Created, p => p.Content);   
}

I started writing a generic HtmlHelper and quickly realized I had problems with value types (memberExpression will be null). The code below attempts to just write out the CSV heading (ID,Created,Content), but it only outputs "Content" (because ID and Created are value types (int and DateTime).

public static void WriteCsv<TModel>(this HtmlHelper htmlHelper, List<TModel> list, params Expression<Func<TModel, object>>[] expressions)
{
    foreach (var expression in expressions)
    {
        MemberExpression memberExpression = expression.Body as MemberExpression;

        if (memberExpression != null)
        {
            var propertyInfo = (PropertyInfo)memberExpression.Member;

            htmlHelper.ViewContext.Writer.Write(propertyInfo.Name + Environment.NewLine);
        }
    }
}

I tried replacing object with dynamic, thinking that would work, but when I quick watch expression.Body, it still seems to think it's dealing with an object (the DebugView property is (System.Object)$p.ID).

Is this impossible in C# 4.0?

Here's the type I'm using it on:

[DataContract(IsReference = true, Namespace = "urn:test:TimeObject")]
public class TimeObject
{
    [DataMember]
    public long ID { get; set; }

    [DataMember]
    public string Content { get; set; }

    [DataMember]
    public DateTime Created { get; set; }
}

回答1:


In the case that the expression references a value type, the compiler has to box the reference; it does so implicitly. This complication means that the expression tree for a Value Type member expression is not simply a MemberExpression, so your cast is returning null.

The following is a general solution for obtaining the property name from a Value Type or Reference Type member expression, taken from this question:

private string GetPropertyName(Expression<Func<object, object>> f) {
    var body = f.Body;
    if (body.NodeType==ExpressionType.Convert)
      body = ((UnaryExpression) body).Operand;
    if ((body as MemberExpression) != null) {
        return (body as MemberExpression).Member.Name;
    }
    return "";
}


来源:https://stackoverflow.com/questions/5707561/using-c-sharp-linq-expressions-for-both-value-types-and-reference-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!