C# 中的可选委托

发布于 2024-11-17 06:39:03 字数 936 浏览 9 评论 0原文

这是两个扩展方法重载的简单示例,

public static class Extended 
{
    public static IEnumerable<int> Even(this List<int> numbers)
    {
        return numbers.Where(num=> num % 2 == 0);
    }

    public static IEnumerable<int> Even(this List<int> numbers, Predicate<int> predicate)
    {
        return numbers.Where(num=> num % 2 == 0 && predicate(num));
    }
}

我希望能够通过将委托设置为可选来将它们合并为一个:

public static class Extended 
{
    public static IEnumerable<int> Even(this List<int> numbers, Predicate<in> predicate = alwaysTrue)
    {
        return numbers.Where(num=> num % 2 == 0 && predicate(num));
    }

    public static bool alwaysTrue(int a) { return true; }
}

但是,编译器会抛出错误:

“谓词”的默认参数值必须是编译时常量

我不明白我的alwaysTrue函数为什么不是常量,但是嘿,编译器知道得更好:)

有没有办法使委托参数可选?

This is a simple example of two extension methods overloads

public static class Extended 
{
    public static IEnumerable<int> Even(this List<int> numbers)
    {
        return numbers.Where(num=> num % 2 == 0);
    }

    public static IEnumerable<int> Even(this List<int> numbers, Predicate<int> predicate)
    {
        return numbers.Where(num=> num % 2 == 0 && predicate(num));
    }
}

I'd like to be able to merge them into one, by setting a delegate to be optional:

public static class Extended 
{
    public static IEnumerable<int> Even(this List<int> numbers, Predicate<in> predicate = alwaysTrue)
    {
        return numbers.Where(num=> num % 2 == 0 && predicate(num));
    }

    public static bool alwaysTrue(int a) { return true; }
}

However, compiler throws an error:

Default parameter value for 'predicate' must be a compile-time constant

I don't see how my alwaysTrue function is not constant, but hey, compiler knows better :)

Is there any way to make the delegate parameter optional?

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

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

发布评论

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

评论(4

人│生佛魔见 2024-11-24 06:39:03

它不是常量,因为您从方法组创建了委托...就 C# 语言而言,这不是编译时常量。

如果您不介意稍微滥用 null 的含义,您可以使用:(

private static readonly Predicate<int> AlwaysTrue = ignored => true;

public static List<int> Even(this List<int> numbers,
                             Predicate<int> predicate = null)
{
    predicate = predicate ?? AlwaysTrue;
    return numbers.Where(num=> num % 2 == 0 && predicate(num));
}

您仍然可以使 AlwaysTrue 成为一个方法并使用方法组转换,但通过仅创建一次委托实例,上述方法的效率稍微更高。)

It's not constant because you've created a delegate from a method group... that's not a compile-time constant as far as the C# language is concerned.

If you don't mind abusing the meaning of null slightly you could use:

private static readonly Predicate<int> AlwaysTrue = ignored => true;

public static List<int> Even(this List<int> numbers,
                             Predicate<int> predicate = null)
{
    predicate = predicate ?? AlwaysTrue;
    return numbers.Where(num=> num % 2 == 0 && predicate(num));
}

(You could still make AlwaysTrue a method and use a method group conversion, but the above approach is very slightly more efficient by creating the delegate instance just once.)

夏末 2024-11-24 06:39:03

您所要做的就是允许它为null,然后将其视为始终为真

为此,您有两种选择,将代码加倍以省略委托调用,这在您不传递委托的情况下执行得更快。

public static List<int> Even(this List<int> numbers, Predicate<int> predicate = null)
{
    if (predicate == null)
        return numbers.Where(num=> num % 2 == 0).ToList();
    else
        return numbers.Where(num=> num % 2 == 0 && predicate(num)).ToList();
}

或者,根据需要提供一个虚拟实现:

public static List<int> Even(this List<int> numbers, Predicate<int> predicate = null)
{
    predicate = predicate ?? new Predicate<int>(alwaysTrue);
    return numbers.Where(num=> num % 2 == 0 && predicate(num)).ToList();
}

另外,请考虑您是否真的想这样做。可选参数对编译代码的影响是调用代码现在提供默认值,这意味着它将始终调用采用列表和委托的重载。

如果您稍后想要返回,则需要确保重新编译调用该方法的所有代码,因为它不会神奇地开始使用不提供委托的方法。

换句话说,这个调用:

var even = list.Even();

看起来会像这样写:

var even = list.Even(null);

如果您现在再次更改要重载的方法,如果上面的调用没有重新编译,那么它将始终调用带有委托的方法,只需提供该参数的null

What you have to do is allow it to be null, and then treat that as always true.

You have two options for this, double the code to leave out the delegate call, this will perform faster in the cases where you don't pass the delegate.

public static List<int> Even(this List<int> numbers, Predicate<int> predicate = null)
{
    if (predicate == null)
        return numbers.Where(num=> num % 2 == 0).ToList();
    else
        return numbers.Where(num=> num % 2 == 0 && predicate(num)).ToList();
}

Or, provide a dummy implementation as you wanted:

public static List<int> Even(this List<int> numbers, Predicate<int> predicate = null)
{
    predicate = predicate ?? new Predicate<int>(alwaysTrue);
    return numbers.Where(num=> num % 2 == 0 && predicate(num)).ToList();
}

Also, consider whether you really want to do this. The effect optional parameters have on compiled code is that the calling code now provides the default value, which means it will always call the overload which take a list and a delegate.

If you later on want to go back, you need to ensure all code that calls the method is recompiled, since it won't magically start using the method that doesn't provide a delegate.

In other words, this call:

var even = list.Even();

Will look like it was written like this:

var even = list.Even(null);

If you now change the method to be overloaded yet again, if the above call isn't recompiled, then it will always call the one with a delegate, just providing null for that parameter.

土豪 2024-11-24 06:39:03

您可以使用 null-默认值:

public static class Extended 
{
    public static IEnumerable<int> Even(this IEnumerable<int> numbers, 
                                        Predicate<int> predicate = null)
    {
        if (predicate==null)
        {
            predicate = i=>true;
        }

        return numbers.Where(num => num % 2 == 0 && predicate(num));
    }
}

You could use a null-default value:

public static class Extended 
{
    public static IEnumerable<int> Even(this IEnumerable<int> numbers, 
                                        Predicate<int> predicate = null)
    {
        if (predicate==null)
        {
            predicate = i=>true;
        }

        return numbers.Where(num => num % 2 == 0 && predicate(num));
    }
}
堇年纸鸢 2024-11-24 06:39:03
public static List<int> Even(this List<int> numbers, Predicate<in> predicate = null)
{
    return numbers.Where(num=> num % 2 == 0 && (predicate == null || predicate(num)));
}
public static List<int> Even(this List<int> numbers, Predicate<in> predicate = null)
{
    return numbers.Where(num=> num % 2 == 0 && (predicate == null || predicate(num)));
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文