C# LINQ使用匿名类型构建表达式

8

我有一段代码,它只构建一个“Name”属性的列表。如何修改代码使其可以构建包含两个属性“Name”和“Test_Result”的列表?我知道可以使用匿名类型来执行此操作,但如何将它们放入动态表达式中呢?以下是我的代码:

string item = "Name";
string item2 = "Test_Result";
Type studentType = typeof(Student);

ParameterExpression itemParam = Expression.Parameter(studentType, item);
MemberInfo itemProperty = studentType.GetProperty(item);

MemberExpression valueInItemField = 
    Expression.MakeMemberAccess(itemParam, itemProperty);

Expression<Func<Student, string>> selectExpression =
    Expression<Func<Student, string>>
        .Lambda<Func<Student, string>>(valueInItemField, itemParam);

IEnumerable<string> currentItemFields = 
    DeserializedStudents.Select(selectExpression.Compile());

关于您最近的重复问题,请在这里和之前的问题得到答案后澄清仍不清楚的内容。如果您告诉我们哪些方面不清楚,我们可能会进行澄清。 - Marc Gravell
1个回答

30

我假设这里的 "Name" 和 "Test_Result" 是灵活的,不能硬编码。

匿名类型是完全定义的常规类;有趣的是编译器提供细节而不是你自己提供。

我建议处理这种情况的方法是使用 Tuple.Create 创建一个IEnumerable<Tuple<string,string>>并将它们称为Item1Item2(从Tuple<,>中获取名称)。另一种选择是使用类似于ExpandoObject的东西,然后使用IDictionary<string,object> API或dynamic API来取回值。

例如:

string item1 = "Name";
string item2 = "Test_Result";
Type studentType = typeof(Student);

var itemParam = Expression.Parameter(studentType, "x");
var member1 = Expression.PropertyOrField(itemParam, item1);
var member2 = Expression.PropertyOrField(itemParam, item2);
var selector = Expression.Call(typeof(Tuple), "Create",
    new[] { member1.Type, member2.Type }, member1, member2);
var lambda = Expression.Lambda<Func<Student, Tuple<string,string>>>(
    selector, itemParam);

var currentItemFields = students.Select(lambda.Compile());

下面是将其投射到具有成员nameresult的自定义类型:

class ProjectedData
{
    public string name { get; set; }
    public string result { get; set; }
}

...

string item1 = "Name";
string item2 = "Test_Result";
Type studentType = typeof(Student);

var itemParam = Expression.Parameter(studentType, "x");
var member1 = Expression.PropertyOrField(itemParam, item1);
var member2 = Expression.PropertyOrField(itemParam, item2);
var selector = Expression.MemberInit(Expression.New(typeof(ProjectedData)),
    Expression.Bind(typeof(ProjectedData).GetMember("name").Single(), member1),
    Expression.Bind(typeof(ProjectedData).GetMember("result").Single(), member2)
);
var lambda = Expression.Lambda<Func<Student, ProjectedData>>(
    selector, itemParam);

var currentItemFields = students.Select(lambda.Compile());

或者使用字典的方法:

string[] fields = {"Name", "Test_Result"};
Type studentType = typeof(Student);

var itemParam = Expression.Parameter(studentType, "x");

var addMethod = typeof(Dictionary<string, object>).GetMethod(
    "Add", new[] { typeof(string), typeof(object) });
var selector = Expression.ListInit(
        Expression.New(typeof(Dictionary<string,object>)),
        fields.Select(field => Expression.ElementInit(addMethod,
            Expression.Constant(field),
            Expression.Convert(
                Expression.PropertyOrField(itemParam, field),
                typeof(object)
            )
        )));
var lambda = Expression.Lambda<Func<Student, Dictionary<string,object>>>(
    selector, itemParam);

var currentItemFields = students.Select(lambda.Compile());

我是指我想将var students = DeserializedStudents.Select(cust => new { name = cust.Name, result = cust.Test_Result });更改为动态表达式。 - Yarik
@Yarik,你需要定义自己的类型;如果你愿意在某个地方定义一个具有成员“name”和“result”的类型,那么可以创建一个类。我可以马上给你举个例子(已完成)。 - Marc Gravell
1
@Yarik,就我个人而言,我会用这些值来填充一个字典。你需要一个例子吗? - Marc Gravell
@Yarik 在底部添加了 - Marc Gravell
你好 Mark!如果我需要几乎相同的东西,但希望将结果作为匿名类型的列表返回怎么办?例如,我如何模拟这样的情况 Expression<Func<Student, object>> exp = s => new { Name = s.Name, Result= s.Test_Result }; 以便稍后在这里使用 students.Select(exp).ToList(); - Mr. Pumpkin
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接