具有匿名类型的 C# LINQ 构建表达式

Posted

技术标签:

【中文标题】具有匿名类型的 C# LINQ 构建表达式【英文标题】:C# LINQ build expression with anonymous type 【发布时间】:2012-12-22 06:18:33 【问题描述】:

我的代码仅使用一个属性“名称”构建列表。 如何修改代码,使其可以使用两个属性“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());

【问题讨论】:

是你最近的重复;请澄清这里的答案和您之前的问题后仍然不清楚的地方。如果您告诉我们不清楚的地方,我们可能会澄清。 【参考方案1】:

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

匿名类型是完全定义的常规类;关于它们的唯一有趣的事情是编译器提供了细节而不是你。

我建议处理这种情况的方法是使用Tuple.Create 创建一个IEnumerable&lt;Tuple&lt;string,string&gt;&gt; 并将它们称为Item1Item2(来自Tuple&lt;,&gt; 的名称。另一个选项是是使用ExpandoObject 之类的东西,然后使用IDictionary&lt;string,object&gt; 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 你必须定义自己的类型才是重点;如果您乐于在某处使用成员nameresult 定义某种类型,那么很好:只需创建一个class。我可以稍后添加一个示例(完成)。 您能再告诉我一件事吗?如果不知道我需要选择多少项目,我该怎么办。我的意思是一次可以是两个项目,下一次可以是四个项目,依此类推。我应该创建一些包含这些项目的列表还是什么? @Yarik 我个人会用这些值填充字典。你想要一个例子吗? @Yarik 添加在底部

以上是关于具有匿名类型的 C# LINQ 构建表达式的主要内容,如果未能解决你的问题,请参考以下文章

C#图解教程 第十九章 LINQ

C# 知识体系构建| | C# 3.0 上

如何使用表达式构建匿名类型?

如何创建 LINQ 表达式树以选择匿名类型

如何使用 Lambda 或 Linq 将匿名类型转换为原始类型成员

在 LINQ 查询中将 KeyValuePair 转换为匿名类型