如何将谓词作为参数传递#
How to pass a predicate as parameter c#
我怎样才能将谓词传递给方法,并且在没有传递谓词的情况下也能正常工作?我想可能是这样的,但它似乎不正确。
private bool NoFilter() { return true; }
private List<thing> GetItems(Predicate<thing> filter = new Predicate<thing>(NoFilter))
{
return rawList.Where(filter).ToList();
}
private List<thing> GetItems(Func<thing, bool> filter = null)
{
return rawList.Where(filter ?? (s => true)).ToList();
}
在此表达式中,s => true
是后备过滤器,如果参数 filter
为空,则对其进行评估。它只需要列表的每个条目(如 s
)和 returns true
.
这有两个部分。
首先,您需要调整NoFilter()
函数以兼容Predicate<T>
。注意后者是通用的,但前者不是。使 NoFilter()
看起来像这样:
private bool NoFilter<T>(T item) { return true; }
我知道您从不使用泛型类型参数,但有必要使其与您的谓词签名兼容。
为了好玩,您也可以这样定义 NoFilter
:
private Predicate<T> NoFilter = x => true;
现在是第二部分:我们可以看看使用新的泛型方法作为 GetItems()
的默认参数。这里的技巧是你只能使用 constants。虽然 NoFilter()
永远不会改变,但从编译器的角度来看,这与形式常量并不完全相同。事实上,只有 一个 可能的常量可以用于此:null
。这意味着您的方法签名必须如下所示:
private List<thing> GetItems(Predicate<thing> filter = null)
然后你可以在函数的开头检查 null
并将其替换为 NoFilter
:
private List<thing> GetItems(Predicate<thing> filter = null)
{
if (filter == null) filter = NoFilter;
return rawList.Where(filter).ToList();
}
如果您还想在调用方法时将其显式传递给方法,则如下所示:
var result = GetItems(NoFilter);
这应该可以完全回答最初的问题,但我不想就此打住。让我们看得更深。
既然你现在无论如何都需要 if
条件,在这一点上我倾向于完全删除 NoFilter<T>()
方法,然后这样做:
private IEnumerable<thing> GetItems(Predicate<thing> filter = null)
{
if (filter == null) return rawList;
return rawList.Where(filter);
}
请注意,我还更改了 return 类型并删除了最后的 ToList()
调用。如果您发现自己在函数末尾调用 ToList()
只是为了匹配 List<T>
return 类型,几乎总是 much 更好地更改方法签名改为 return IEnumerable<T>
。如果你真的需要一个列表(通常你不需要),你总是可以在调用函数之后调用 ToList()
。
此更改使您的方法更有用,因为它为您提供了一个更抽象的类型,该类型将与其他接口更兼容,并且它可能会为您带来显着的性能提升,无论是在降低内存使用还是在惰性评估条款。
这里的最后一个补充是,如果你确实缩减到 IEnumerable
,我们现在可以看到这种方法在基础 rawItems
字段之外并没有真正提供太多价值。您可能会考虑转换为 属性,如下所示:
public IEnumerable<T> Items {get {return rawList;}}
这仍然允许您的类型的使用者在需要时通过现有的 .Where()
方法使用谓词(或不使用),同时还继续隐藏底层原始数据(您不能直接调用.Add()
等等)。
我怎样才能将谓词传递给方法,并且在没有传递谓词的情况下也能正常工作?我想可能是这样的,但它似乎不正确。
private bool NoFilter() { return true; }
private List<thing> GetItems(Predicate<thing> filter = new Predicate<thing>(NoFilter))
{
return rawList.Where(filter).ToList();
}
private List<thing> GetItems(Func<thing, bool> filter = null)
{
return rawList.Where(filter ?? (s => true)).ToList();
}
在此表达式中,s => true
是后备过滤器,如果参数 filter
为空,则对其进行评估。它只需要列表的每个条目(如 s
)和 returns true
.
这有两个部分。
首先,您需要调整NoFilter()
函数以兼容Predicate<T>
。注意后者是通用的,但前者不是。使 NoFilter()
看起来像这样:
private bool NoFilter<T>(T item) { return true; }
我知道您从不使用泛型类型参数,但有必要使其与您的谓词签名兼容。
为了好玩,您也可以这样定义 NoFilter
:
private Predicate<T> NoFilter = x => true;
现在是第二部分:我们可以看看使用新的泛型方法作为 GetItems()
的默认参数。这里的技巧是你只能使用 constants。虽然 NoFilter()
永远不会改变,但从编译器的角度来看,这与形式常量并不完全相同。事实上,只有 一个 可能的常量可以用于此:null
。这意味着您的方法签名必须如下所示:
private List<thing> GetItems(Predicate<thing> filter = null)
然后你可以在函数的开头检查 null
并将其替换为 NoFilter
:
private List<thing> GetItems(Predicate<thing> filter = null)
{
if (filter == null) filter = NoFilter;
return rawList.Where(filter).ToList();
}
如果您还想在调用方法时将其显式传递给方法,则如下所示:
var result = GetItems(NoFilter);
这应该可以完全回答最初的问题,但我不想就此打住。让我们看得更深。
既然你现在无论如何都需要 if
条件,在这一点上我倾向于完全删除 NoFilter<T>()
方法,然后这样做:
private IEnumerable<thing> GetItems(Predicate<thing> filter = null)
{
if (filter == null) return rawList;
return rawList.Where(filter);
}
请注意,我还更改了 return 类型并删除了最后的 ToList()
调用。如果您发现自己在函数末尾调用 ToList()
只是为了匹配 List<T>
return 类型,几乎总是 much 更好地更改方法签名改为 return IEnumerable<T>
。如果你真的需要一个列表(通常你不需要),你总是可以在调用函数之后调用 ToList()
。
此更改使您的方法更有用,因为它为您提供了一个更抽象的类型,该类型将与其他接口更兼容,并且它可能会为您带来显着的性能提升,无论是在降低内存使用还是在惰性评估条款。
这里的最后一个补充是,如果你确实缩减到 IEnumerable
,我们现在可以看到这种方法在基础 rawItems
字段之外并没有真正提供太多价值。您可能会考虑转换为 属性,如下所示:
public IEnumerable<T> Items {get {return rawList;}}
这仍然允许您的类型的使用者在需要时通过现有的 .Where()
方法使用谓词(或不使用),同时还继续隐藏底层原始数据(您不能直接调用.Add()
等等)。