【问题标题】:Extension method, Func of T and List of T扩展方法、T 函数和 T 列表
【发布时间】:2012-11-14 06:39:17
【问题描述】:

我想编写一个可以在将值插入列表之前完成一些检查的函数。 例如:

class Person {
    public string Name { get; set; }
    public int Value { get; set; }
    public Guid Id { get; set; }
}
-------
var persons = new List<Person>();
// add a new person if John doesn't exist
persons.AddIf(s => !s.Name.Equals("John"), new Person { ... });
----
public static void AddIf(this List<T> lst, Func<T, bool> check, T data)
{
     // how can I use the Func 'check' to check if exist an object with the
     // information that the client wrote and, if not exists, insert the new value
     // into the list???
     if ( check )
}

我如何使用 Func 'check' 来检查是否存在具有客户端写入信息的对象,如果不存在,则将新值插入列表中?

【问题讨论】:

  • 请注意,在您的示例中,无论谓词结果如何,您都会创建 Person 对象。如果您不打算在检查失败时使用此对象,您可能应该更改方法的签名,以便仅在需要时创建 Person 对象。
  • @PanosRontogiannis 很可能只是一个虚拟变量 OP 放在那里。他很可能已经有一个实例要添加..

标签: c# generics lambda extension-methods


【解决方案1】:

您需要使您的方法具有通用性。

public static void AddIf<T>(this List<T> lst, Func<T, bool> check, T data)
{
    if (!lst.All(check))
        return;

    lst.Add(data);
}

和你想要的用法(所有项目都应该满足谓词):

persons.AddIf(s => !s.Name.Equals("John"), new Person { ... });

【讨论】:

  • 为了概括,我会使用 Expression>。方法的语义不好。如果不是条件,则添加。
  • @KirillBestemyanov 同意并删除。顺便说一句,表达式树在这里会带来什么好处?
  • All 将失败,如果任何不满足谓词(即它的名字是约翰),我们不会添加新的人。
  • 好的...所以我需要通过条件检查我的数据...为什么要使用表达式??
  • @Gaetanu 你不应该在这里使用表达式。
【解决方案2】:

对于您自己的语义,您已经有了一个可以依赖的答案。但对我来说,方法命名越是想起来就越模糊。 AddIf 表示如果有则添加或全部添加?在你的情况下,这就是全部。所以你应该更好地命名它。 AddIfAll 什么的。

如果某些内容不存在,则添加到列表中是一个常见的要求,而我的建议是为了您的目的而更加充实(我相信),这使得调用方更容易。

可能

public static bool AddIfNotContains<S, T>(this ICollection<S> lstObject, 
                                          Func<S, T> selector, T valueToMatch,
                                          S objectToAdd)
{
    if (lstObject.Contains(selector, valueToMatch))
        return false;

    lstObject.Add(objectToAdd);
    return true;
}

我更喜欢在我的程序中使用一个小的 Contains 重载:

public static bool Contains<S, T>(this IEnumerable<S> lstObject, 
                                  Func<S, T> comparer, T valueToMatch)
{
    return lstObject.Any(s => comparer(s).Equals(valueToMatch));
}

这避免了每次从我们这边编写Equals 运算符的麻烦。

您可以致电:

persons.AddIfNotContains(s => s.Name, "John", new Person { ... });

我认为这使得语法更简单。

注意:

我希望你知道这里的问题。你写的很好

persons.AddIfNotContains(s => s.Name, "John", new Person { Name = "Serena", .. });

即使已经存在名为 Serena 的人,因为您在此处检查的是 John。如果这对你来说没问题,那就太好了。 如果我正确理解您的问题,更好的实施方式是

public static bool AddIfTrulyNotContains<S, T>(this ICollection<S> lstObject, 
                                               Func<S, T> selector, S objectToAdd)
{
    if (lstObject.Contains(selector, selector(objectToAdd)))
        return false;

    lstObject.Add(objectToAdd);
    return true;
}

现在您可以轻松拨打电话了:

persons.AddIfTrulyNotContains(s => s.Name, new Person { Name = "John", .. });

这只是检查John 并在列表中没有John 时添加。此外,我将返回类型设为bool 以表示添加。

【讨论】:

    【解决方案3】:

    从上面的答案中快速组合/改变,有一些 cmets:(我的代表太低了,不能简单地评论 脸红

    因此,根据问题示例中的某些细节:

    如何使用 Func 'check' 来检查是否存在具有 客户写的信息,如果不存在,插入新的 值到列表中?

    1) 在添加到列表(而不是打开“检查”)之前,我肯定会考虑创建/使用专门用于检查重复项(如前所述,一种常见操作)的通用扩展,所以可能是这样的:

    public static void AddIfDistinct<T>(this List<T> lst, T data, Func<T, bool> dupeCheck)
    {
        if (lst.Any(dupeCheck))
            return;
    
        lst.Add(data);
    }
    

    2)根据要求,在我看来,“任何”在语义上比“全部”更清晰 - 我们正在检查“任何”重复项,而不是检查“所有”当前项目是否“不是”重复...这听起来可能微不足道,但我认为可以肯定地说它更明显。这显然意味着你传入的 lambda 看起来像这样(这里的语义变化意味着你真的必须让扩展方法命名好)

    s => s.Name.Equals("John")
    

    3) 最后的评论,如果合适的话,你也可以覆盖你的类上的一些东西,比如“CompareTo”等,并创建一个真正通用的“AddToListIfUnique”等

    此外,在您的示例中使用 Expression> 根本无济于事(正如有人建议的那样),因为无论如何您都在使用 List (只有在使用 IQueryable 等时才值得)

    【讨论】:

    • 即使名称可能会导致错误。 IEnumerable.All() 方法不会遍历整个列表。当给定的函数返回 false 时,它​​会立即中止。它与IEnumerable.Any() 完全相反,后者在给定函数返回 true 时立即返回。所以写if(!lst.Any(func)) 会迭代与if(lst.All(func)) 一样多的项目。看看 MSDN 上的备注部分 AnyAll
    • 是的,100% 正确,业余的困惑。然而,我仍然会非常强烈地支持它,因为它更清楚地表明了正在努力实现的目标。将编辑以反映这一点...谢谢。
    猜你喜欢
    • 1970-01-01
    • 2011-07-28
    • 2014-06-07
    • 2011-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-06
    • 1970-01-01
    相关资源
    最近更新 更多