使用反射在谓词中引用 class 的 属性

Reference to a property of class in predicate using reflection

我有一个简单的 class 有许多 DateTime 属性:

public class MyClass
{
    public DateTime Created {get; set;}
    public DateTime LastChanged {get; set;}
    public DateTime LastAccessed {get; set;}
    ...
}

稍后,在我的代码中的某处,我想根据这些属性过滤 MyClass 的集合。对于每个 属性 我想做这样的事情:

myQueryableOfMyClass = myQueryableOfMyClass.Where(a => ((begin == null) || (a.Created >= begin)) && ((end == null) || (a.Created <= end));

如果我必须为我的每个 DateTime 属性 执行此操作,那将是很多代码,并且存在拼写错误的风险,所以我想做这样的事情:

myQueryableOfMyClass = Filter(myQueryableOfMyClass, begin, end, MyClass.Created);
myQueryableOfMyClass = Filter(myQueryableOfMyClass, changebegin, changeend, MyClass.LastChanged);
myQueryableOfMyClass = Filter(myQueryableOfMyClass, accbegin, accend, MyClass.LastAccessed);
...

其中 Filter 方法是使用 LINQ Where 实现的,如第一个示例。

上面的代码当然不能编译,因为MyClass.Created是胡说八道

肯定有一些使用反射的解决方案,但我对反射的经验很少。你能帮帮我吗?

您可以使用 Func<MyClass, DateTime> 来选择要使用的 属性,如下所示:

public static IEnumerable<MyClass> Filter
(
    IEnumerable<MyClass> myClasses, 
    DateTime? begin, 
    DateTime? end, 
    Func<MyClass, DateTime> selector
)
{
    return myClasses.Where(a => ((begin == null) || (selector(a) >= begin)) && ((end == null) || (selector(a)) <= end));
}

你可以这样称呼它:

Filter(myClasses, begin, end, myClass => myClass.Created);

myClass => myClass.Created 替换为要使用的 属性 的选择器。

创建辅助方法

        public void test()
        {
           myQueryableOfMyClass = myQueryableOfMyClass.Where(a => Filter(begin, end, a));
        }



        public Boolean Filter(DateTime begin, DateTime end, DateTime date)
        {
            if(((begin == null) || (date >= begin)) && ((end == null) || (date <= end)))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }​

我非常喜欢可读代码,其中方法名称解释了它们的作用。出于这个原因,我建议添加以下方法。它们可以像我的示例一样添加到数据 class 本身中,也可以添加到 "Filter" class.

public class MyClass
{
    public DateTime Created { get; set; }
    public DateTime LastChanged { get; set; }
    public DateTime LastAccessed { get; set; }

    public bool WasCreatedInTimespan(DateTime? begin, DateTime? end)
    {
        return IsDateInTimespan(Created, begin, end);
    }

    public bool WasChangedInTimespan(DateTime? begin, DateTime? end)
    {
        return IsDateInTimespan(LastChanged, begin, end);
    }

    public bool WasAccessedInTimespan(DateTime? begin, DateTime? end)
    {
        return IsDateInTimespan(LastAccessed, begin, end);
    }

    private static bool IsDateInTimespan(DateTime date, DateTime? begin, DateTime? end)
    {
        return (!begin.HasValue || date >= begin.Value) && (!end.HasValue || date >= end.Value);
    }
}

有了这些方法,调用代码就变得非常清晰了。像这样:

var createdList = myQueryableOfMyClass.Where(m => m.WasCreatedInTimespan(begin, end));
var changedList = myQueryableOfMyClass.Where(m => m.WasChangedInTimespan(begin, end));
var accessedList = myQueryableOfMyClass.Where(m => m.WasAccessedInTimespan(begin, end));