【问题标题】:How do you pass multiple functions as constrainment parameters in C#?如何在 C# 中将多个函数作为约束参数传递?
【发布时间】:2013-01-29 13:34:36
【问题描述】:

对于我正在尝试学习的内容,这可能是一个措辞不当的问题,所以我将解释我正在尝试做什么。我使用以下表达式创建了以下列表:

var showIndexes = from item in lItems
                               where WildcardString.IsMatch(item.RecordId.ToString(), filter) ||
                                     WildcardString.IsMatch(item.Tokens.ToString(), filter) ||
                                     WildcardString.IsMatch(string.Format("{0}:{1}", item.OwnerID.SystemName, item.OwnerID.Port.ToString()), filter) ||
                                     WildcardString.IsMatch(item.ChildID.UserName, filter) ||
                                     WildcardString.IsMatch(item.TypeOfLicense.ToString(), filter) ||
                                     WildcardString.IsMatch(item.ExpiryTime.DateTime.ToLocalTime().ToString(), filter)
                               select item.RecordId.ToString();

我想将此表达式放入一个函数中,并将“where”中使用的约束作为单个参数传递。我如何实现这一目标?在处理文件属性(当您选择不同的属性枚举时)时,我已经看到使用按位 OR 的类似功能,所以我相信应该可以编写我自己的函数,该函数将一组委托作为“where”规则。

如果相关,WildcardString.IsMatch(string, string) 是一个布尔函数。此外,我有三个不同的函数执行完全相同的操作,但在不同的列表中,这就是为什么我想学习如何将其简化为单个函数,只需将列表和选择规则传入。

【问题讨论】:

    标签: c# linq .net-4.0 methods delegates


    【解决方案1】:

    只需定义一个谓词:一个接受 item 并返回布尔值的函数。

    假设itemRecord 类型并且filter 在范围内:

    Func<Record, bool> predicate = r =>
        WildCardString.IsMatch(r.RecordId.ToString(), filter) ||
        WildCardString.IsMatch(...);
    

    然后您可以将predicate 视为一流值(存储它,将其作为参数传递等)并过滤任何IEnumerable&lt;Record&gt;

    var filtered = enumerable.Where(predicate);
    

    【讨论】:

    • @BasedAsFunk:您应该花一些时间来了解 lambda 以及它们如何捕获其环境(在此示例中,环境是 filter)以确保没有意外。一个非常强大的工具,但一如既往,您需要注意如何使用它。
    • 我过去使用过它们,但我对它们一无所知。它在我的待办事项清单上
    【解决方案2】:

    只需将 where 部分重构为一个函数:

    public bool MyIsMatch(MyType item, String filter)
    {
        return
        WildcardString.IsMatch(item.RecordId.ToString(), filter) ||
        WildcardString.IsMatch(item.Tokens.ToString(), filter) ||
        WildcardString.IsMatch(string.Format("{0}:{1}", item.OwnerID.SystemName, item.OwnerID.Port.ToString()), filter) ||
        WildcardString.IsMatch(item.ChildID.UserName, filter) ||
        WildcardString.IsMatch(item.TypeOfLicense.ToString(), filter) ||
        WildcardString.IsMatch(item.ExpiryTime.DateTime.ToLocalTime().ToString(), filter);
    }
    

    然后调用它:

    var showIndexes = from item in lItems where MyIsMatch(item, filter) select item.ToString();
    

    【讨论】:

    • 这对我不起作用的唯一原因是因为现在我需要 3 个 MyIsMatch 函数。我想创建一个函数并动态创建约束参数。
    【解决方案3】:

    您可以创建一个带有有效值列表的 Is.Match 版本

    public static bool IsMatch(IEnumerable<object> values, filter) {
        return values.All(v => IsMatch(v.ToString(),filter))
    }
    

    在你的情况下,你会像这样使用它

    var values = new object[]{
                        item.RecordId,
                        item.Tokens,
                        string.Format("{0}:{1}", item.OwnerID.SystemName, item.OwnerID.Port),
                        item.ChildID.UserName
                        item.TypeOfLicense
                        item.ExpiryTime.DateTime};
    
    var showIndexes = from item in lItems
                      where WildcardString.IsMatch(values,filter)
                               select item.RecordId.ToString();
    

    我选择使用IEnumerable&lt;object&gt; 而不是IEnumerable&lt;string&gt; 来从每个项目中删除.ToString(),但这确实会带来一定的成本,因为您将对任何ValueTyped 值进行装箱和拆箱。如果它位于已确定的性能关键路径上,则应改用 IEnumerable&lt;string&gt;

    【讨论】:

    • 我不太精通 lambda 表达式,但 VS 在你的 IsMatch 函数中抱怨 v.ToString
    • @BasedAsFunk 因为拼写错误而抱怨,现在已修复 :)
    • 哈哈,我以为这就是你的意思,但我对这个问题了解得不够多,不知道你是否犯了不同的错误哈哈
    • @BasedAsFunk 您可以像声明一个函数一样阅读 lambda。 => 之前的所有内容都是参数名称(您通常不必键入它们,但您可以) => 之后的所有内容都是方法主体,如果它是一个表达式,您可以省略 { 和 },如果它不止一个,则返回表达式/语句,您必须像往常一样在主体周围使用 {} 并显式返回(如果您需要在这种情况下返回一个值)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-05
    • 2020-02-10
    • 2016-10-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多