我可以获得扩展方法的 Func 对象吗

发布于 2024-12-08 11:41:49 字数 2831 浏览 1 评论 0原文

我有一个小型实用程序扩展方法,它对 IEnumerable中的某些 LINQ 扩展方法执行一些 null 检查。代码如下所示

public static class MyIEnumerableExtensions
{
    // Generic wrapper to allow graceful handling of null values
    public static IEnumerable<T> NullableExtension<T>(this IEnumerable<T> first, IEnumerable<T> second, Func<IEnumerable<T>, IEnumerable<T>, IEnumerable<T>> f)
    {
        if (first == null && second == null) return Enumerable.Empty<T>();
        if (first == null) return second;
        if (second == null) return first;
        return f(first, second);
    }

    // Wrap the Intersect extension method in our Nullable wrapper
    public static IEnumerable<T> NullableIntersect<T>(this IEnumerable<T> first, IEnumerable<T> second)
    {
        // It'd be nice to write this as
        //
        //   return first.NullableExtension<T>(second, IEnumerable<T>.Intersect );
        //
        return first.NullableExtension<T>(second, (a,b) => a.Intersect(b));
    }
}

那么,有没有办法将 IEnumerable.Intersect 扩展方法直接传递给 NullableExtension 而不是将其包装在 lambda 中?

编辑

因为传入 Enumerable 扩展方法实际上很简洁,所以我删除了 NullableIntersect (和其他)方法,只调用可为 null 的包装器直接地。

此外,正如 Anthony 指出的,Empty 枚举应该执行的语义根据扩展方法的不同而不同,即 UnionIntersect。因此,我将 NullableExtension 方法重命名为 IgnoreIfNull,这样可以更好地反映通用行为。

public static class MyIEnumerableExtensions
{
    // Generic wrappers to allow graceful handling of null values
    public static IEnumerable<T> IgnoreIfNull<T>(this IEnumerable<T> first, IEnumerable<T> second, Func<IEnumerable<T>, IEnumerable<T>, IEnumerable<T>> f)
    {
        if (first == null && second == null) return Enumerable.Empty<T>();
        if (first == null) return second;
        if (second == null) return first;
        return f(first, second);
    }

    public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> first, IEnumerable<T> second, Func<IEnumerable<T>, IEnumerable<T>, IEnumerable<T>> f)
    {
        return f(first ?? Enumerable.Empty<T>(), second ?? Enumerable.Empty<T>());
    }

}

// Usage example.  Returns { 1, 4 } because arguments to Union and Intersect are ignored
var items = new List<int> { 1, 4 };
var output1 = items.IgnoreIfNull(null, Enumerable.Union).IgnoreIfNull(null, Enumerable.Intersect);

// Usage example.  Returns { } because arguments to Union and Intersect are set to empty
var output2 = items.EmptyIfNull(null, Enumerable.Union).EmptyIfNull(null, Enumerable.Intersect);

I have a small utility extension method that performs some null checks on some LINQ extension methods in IEnumerable<T>. The code looks like this

public static class MyIEnumerableExtensions
{
    // Generic wrapper to allow graceful handling of null values
    public static IEnumerable<T> NullableExtension<T>(this IEnumerable<T> first, IEnumerable<T> second, Func<IEnumerable<T>, IEnumerable<T>, IEnumerable<T>> f)
    {
        if (first == null && second == null) return Enumerable.Empty<T>();
        if (first == null) return second;
        if (second == null) return first;
        return f(first, second);
    }

    // Wrap the Intersect extension method in our Nullable wrapper
    public static IEnumerable<T> NullableIntersect<T>(this IEnumerable<T> first, IEnumerable<T> second)
    {
        // It'd be nice to write this as
        //
        //   return first.NullableExtension<T>(second, IEnumerable<T>.Intersect );
        //
        return first.NullableExtension<T>(second, (a,b) => a.Intersect(b));
    }
}

So, is there a way to pass in the IEnumerable<T>.Intersect extension method to NullableExtension directly rather than wrapping it in a lambda?

Edit

Because it is actually concise to pass in the Enumerable extension method, I removed the NullableIntersect (and other) methods and just call the nullable wrapper directly.

Also, as Anthony points out, the semantics of what an Empty enumerable should do is different depending on the extension method, i.e. Union versus Intersect. As such, I rename the NullableExtension method to IgnoreIfNull which better reflects the generic behavior.

public static class MyIEnumerableExtensions
{
    // Generic wrappers to allow graceful handling of null values
    public static IEnumerable<T> IgnoreIfNull<T>(this IEnumerable<T> first, IEnumerable<T> second, Func<IEnumerable<T>, IEnumerable<T>, IEnumerable<T>> f)
    {
        if (first == null && second == null) return Enumerable.Empty<T>();
        if (first == null) return second;
        if (second == null) return first;
        return f(first, second);
    }

    public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> first, IEnumerable<T> second, Func<IEnumerable<T>, IEnumerable<T>, IEnumerable<T>> f)
    {
        return f(first ?? Enumerable.Empty<T>(), second ?? Enumerable.Empty<T>());
    }

}

// Usage example.  Returns { 1, 4 } because arguments to Union and Intersect are ignored
var items = new List<int> { 1, 4 };
var output1 = items.IgnoreIfNull(null, Enumerable.Union).IgnoreIfNull(null, Enumerable.Intersect);

// Usage example.  Returns { } because arguments to Union and Intersect are set to empty
var output2 = items.EmptyIfNull(null, Enumerable.Union).EmptyIfNull(null, Enumerable.Intersect);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

左岸枫 2024-12-15 11:41:49

Intersect 是在静态类 Enumerable 中定义的。您可以将其传递到您的方法中,如下所示

return first.NullableExtension<T>(second, Enumerable.Intersect);

注意:您可能担心空序列情况下逻辑的行为。例如,在

List<int> first = null;
var second = new List<int> { 1, 4 };
var output = first.NullableIntersect(second).ToList();

您定义的情况下,output 包含 {1, 4}second 的元素)。我可能期望 first 被视为空序列,并且与 second 的交集将导致空序列。最终,您需要决定您想要的行为。

Intersect is defined within static class Enumerable. You can pass it into your method like below

return first.NullableExtension<T>(second, Enumerable.Intersect);

Note: You might be concerned about the behavior of your logic in the case of a null sequence. For example, in the case of

List<int> first = null;
var second = new List<int> { 1, 4 };
var output = first.NullableIntersect(second).ToList();

You have defined it such that output contains {1, 4} (the elements of second). I might expect first to instead be treated as an empty sequence, and an intersection with second would result in an empty sequence. Ultimately, that's for you to decide the behavior you desire.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文