【问题标题】:How to pass a predicate as parameter c#如何将谓词作为参数传递c#
【发布时间】:2017-06-26 13:53:45
【问题描述】:

如何将谓词传递给方法,但如果没有传递谓词,它也可以工作?我想也许是这样,但它似乎不正确。

private bool NoFilter() { return true; }

private List<thing> GetItems(Predicate<thing> filter = new Predicate<thing>(NoFilter))
{
    return rawList.Where(filter).ToList();
}

【问题讨论】:

  • 你要的是default value,对吧?否则,将NoFilter 定义为Predicate&lt;things&gt; and pass it
  • 创建重载GetItems() { return GetItems(NoFilter); }
  • 你可以有一个默认值null,你必须在你的代码中检查它。或者,您可以使用第二个不带谓词的方法重载该方法。
  • @Servy 可能不是重复的,并且 OP 在将委托作为参数传递时遇到问题?
  • 您的默认值必须在编译时解析。 new Predicate&lt;thing&gt;(NoFIlter) 是在运行时评估的,因为那里有构造函数,所以不管你怎么写它都不会工作。

标签: c# delegates predicate


【解决方案1】:
private List<thing> GetItems(Func<thing, bool> filter = null)
{
    return rawList.Where(filter ?? (s => true)).ToList();
}

在此表达式中,s =&gt; true 是回退过滤器,如果参数 filter 为空,则评估该过滤器。它只获取列表中的每个条目(如s)并返回true

【讨论】:

  • 实现Cannot convert from System.Predicate&lt;thing&gt; to System.LinQ.Expressions.Expression&lt;SystemFunc&lt;thing,bool&gt;&gt;时出现这样的错误
  • 试试Func&lt;Thing, bool&gt;而不是Predicate&lt;Thing&gt;
【解决方案2】:

这有两个部分。

首先,您需要调整NoFilter() 函数以兼容Predicate&lt;T&gt;。注意后者是通用的,但前者不是。使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&lt;T&gt;() 方法,然后这样做:

private IEnumerable<thing> GetItems(Predicate<thing> filter = null)
{
    if (filter == null) return rawList;
    return rawList.Where(filter);
}

请注意,我还更改了返回类型并在最后删除了 ToList() 调用。如果您发现自己在函数末尾调用ToList() 只是为了匹配List&lt;T&gt; 返回类型,则几乎总是much 更好地将方法签名更改为返回IEnumerable&lt;T&gt;。如果你真的需要一个列表(通常你不需要),你可以在调用函数后随时调用ToList()

此更改通过为您提供与其他接口更兼容的更抽象的类型,使您的方法更有用,并且它可能为您带来显着的性能提升,无论是在降低内存使用方面还是在惰性求值。

这里的最后一个补充是,如果你只削减到IEnumerable,我们现在可以看到,除了基本的rawItems 字段之外,这个方法并没有真正提供太多价值。您可能会考虑转换为属性,如下所示:

public IEnumerable<T> Items {get {return rawList;}}

这仍然允许您类型的消费者通过现有的.Where() 方法使用(或不使用)谓词,同时还继续隐藏底层原始数据(您不能直接调用.Add() 等关于这个)。

【讨论】:

  • 我在传递到 .Where() 的后过滤器上出现错误。假设 rawList 是字符串对象的列表...无法将 System.Predicate 转换为 System.Func .. 不能是唯一的吗??
  • 谓词 与 Func 相同。它应该是可转换的,或者可能缺少重载或引用。否则,这是一个简单的更改。
  • 嗯...我觉得我在这里缺少一些基本的东西,甚至无法弄清楚我需要传递给 GetItems() 的东西的形状应该是什么...想我会开一个新的q!
猜你喜欢
  • 2012-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多