创建表达式树以分配给列表的属性

时间:2014-11-06 18:46:47

标签: c# lambda expression-trees

这几天困扰着我......

如果我有一个自己的对象列表SearchResults,并且SearchResults包含多个对象列表,所有这些对象都有匹配(bool)属性,如何重新创建表达式树以实现以下目的:

//searchResults is a List<SearchResults>

searchResults[i].Comments = searchResults[i].Comments.Select(p1 =>
{
    p1.Match = ListOfStringVariable.All(p2 =>
    {
        string value = (string)typeof(CommentData).GetProperty(propertyName).GetValue(p1);
        return value.Contains(p2);
    });
    return p1;
}).OrderByDescending(x => x.Match);

....

public class SearchResults
{
    public IEnumerable<CommentData> Comments { get; set; }
    public IEnumerable<AdvisorData> Advisors { get; set; }
}

public class CommentData
{
    public string CommentText { get; set; }
    public bool Match { get; set; }
}

public class AdvisorData
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool Match { get; set; }
}

需要表达式树,因为我不知道编译时需要分配的属性,无论是注释,顾问等(因为这是对更大问题的简化)。以上示例仅用于注释,那么如何在不使用条件块的情况下使用相同的代码分配给Advisors?

非常感谢

更新

到目前为止,使用反射我们从StriplingWarrior

得到了以下内容
var searchResult = searchResults[i];
foreach (var srProperty in searchResultsProperties)
{
    var collectionType = srProperty.PropertyType;
    if(!collectionType.IsGenericType || collectionType.GetGenericTypeDefinition() != typeof(IEnumerable<>))
    {
        throw new InvalidOperationException("All SearchResults properties should be IEnumerable<Something>");
    }
    var itemType = collectionType.GetGenericArguments()[0];
    var itemProperties = itemType.GetProperties().Where(p => p.Name != "Match");
    var items = ((IEnumerable<IHaveMatchProperty>) srProperty.GetValue(searchResult))
        // Materialize the enumerable, in case it's backed by something that
        // would re-create objects each time it's iterated over.
        .ToList();
    foreach (var item in items)
    {
        var propertyValues = itemProperties.Select(p => (string)p.GetValue(item));
        item.Match = propertyValues.Any(v => searchTerms.Any(v.Contains));
    }
    var orderedItems = items.OrderBy(i => i.Match);
    srProperty.SetValue(srProperty, orderedItems);
}

orderedItems类型为System.Linq.OrderedEnumerable<IHaveMatchProperty,bool>,需要转换为IEnumerable<AdvisorData>。以下抛出错误:

&#39; System.Linq.Enumerable.CastIterator(System.Collections.IEnumerable)&#39;是一种方法&#39;但是像'&#39;

一样使用
var castMethod = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(new[] {propertyType});
var result = castMethod.Invoke(null, new[] { orderedItems });

其中propertyType的类型为AdvisorData

1 个答案:

答案 0 :(得分:1)

首先,让你的类型实现这个接口,这样你就不必做太多的反思:

public interface IHaveMatchProperty
{
    bool Match { get; set; }
}

然后编写代码来做这样的事情。 (我做了很多假设,因为你的问题并不是很清楚你的预期行为是什么。)

var searchResult = searchResults[i];
foreach (var srProperty in searchResultsProperties)
{
    var collectionType = srProperty.PropertyType;
    if(!collectionType.IsGenericType || collectionType.GetGenericTypeDefinition() != typeof(IEnumerable<>))
    {
        throw new InvalidOperationException("All SearchResults properties should be IEnumerable<Something>");
    }
    var itemType = collectionType.GetGenericArguments()[0];
    var itemProperties = itemType.GetProperties().Where(p => p.Name != "Match");
    var items = ((IEnumerable<IHaveMatchProperty>) srProperty.GetValue(searchResult))
        // Materialize the enumerable, in case it's backed by something that
        // would re-create objects each time it's iterated over.
        .ToList();
    foreach (var item in items)
    {
        var propertyValues = itemProperties.Select(p => (string)p.GetValue(item));
        item.Match = propertyValues.Any(v => searchTerms.Any(v.Contains));
    }
    var orderedItems = items.OrderBy(i => i.Match);
    srProperty.SetValue(srProperty, orderedItems);
}