此代码正确返回一行:
_loadedAssemblies.ForEach(x =>
{
foundTypes.AddRange(from t in x.GetTypes()
where t.GetInterfaces().Contains(typeof(TInterface))
&& t.BaseType.Name.LeftOf('`') == baseClass.Name.LeftOf('`')
select t);
}
但是,当我使用PredicateBuilder时,我得到零行:
var compiledPredicate = CompiledPredicate<TInterface>();
_loadedAssemblies.ForEach(x =>
{
foundTypes.AddRange(from t in x.GetTypes()
where compiledPredicate.Invoke(typeof(TInterface))
select t);
}
private static Func<Type, bool> CompiledPredicate<T>() where T : class
{
// True means all records will be returned if no other predicates are applied.
var predicate = PredicateBuilder.True<Type>();
// Get types that implement the interface (T).
predicate = predicate.And(t => t.GetInterfaces().Contains(typeof(T)));
// If the config file includes filtering by base class, then filter by it.
if (!string.IsNullOrWhiteSpace(_baseClass))
{
Type baseClass = Type.GetType(_baseClass);
predicate = predicate.And(t => t.BaseType.Name.LeftOf('`') == baseClass.Name.LeftOf('`'));
}
return predicate.Compile();
}
Someone suggested创建我的循环变量的副本,但我试过了,我仍然得到零行。我不确定为什么使用PredicateBuilder不返回任何行。知道我错过了什么吗?
答案 0 :(得分:1)
您在评论中提到的更改(foundTypes.AddRange(x.GetTypes().AsQueryable().Where(compiledPredicate));
)与您使用AsQueryable
的事实毫无关系。在第一种情况下,您将硬编码类型传递给谓词的每个调用,在第二种情况下,您将从序列中传递给定项。如果您删除了AsQueryable
并使用了Enumerable.Where
它也可以使用,或者在调用它时也没有硬编码类型,而是通过了当前项目。
所以你可以这样做:
foundTypes.AddRange(x.GetTypes().Where(compiledPredicate));
此外,在创建谓词时,不需要做你正在做的那么多工作。表达式需要相当多的额外工作才能处理。使用linq到对象,你只需要处理代表,这些代表不那么挑剔。
private static Func<Type, bool> CompiledPredicate<T>() where T : class
{
Func<Type, bool> predicate = t => t.GetInterfaces().Contains(typeof(T));
if (!string.IsNullOrWhiteSpace(_baseClass))
{
Type baseClass = Type.GetType(_baseClass);
return t => predicate(t) &&
t.BaseType.Name.LeftOf('`') == baseClass.Name.LeftOf('`');
}
return predicate;
}