确保 varargs 方法至少需要一个参数的最干净方法是什么

本文关键字:方法 参数 一个 是什么 varargs 确保 | 更新日期: 2023-09-27 18:32:52

给定以下扩展方法:

public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, params T[] second)
{
    return second.Concat(first);
}

我可以写:

var foo = new[] { "cd", "ef", }.Prepend("ab");

这会产生所需的:

{ "ab", "cd", "ef", }

太好了,但我也可以写:

var bar = new[] { "cd", "ef", }.Prepend();

这是完全荒谬但仍然有效的代码。我想防止这种滥用我的方法。

我可以将该方法重写为以下内容:

public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, T second, params T[] third)
{
    return new[] { second, }.Concat(third).Concat(first);
}

但随后我必须在正文中决定是否要secondthird结合起来,然后.Concat它们,或者我是否要进行多次.Concat调用。无论哪种方式,都涉及数组创建和额外的方法调用,这不是最佳的。

这个答案提出了一种简单而新颖的方法来解决这个问题 - 声明一个no-args方法重载并将其标记为Obsolete,以便它生成编译错误:

public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, params T[] second)
{
    return second.Concat(first);
}
[Obsolete("Don't use this method, it always throws.", true)]
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first)
{
    throw new InvalidOperationException();
}

现在使用没有参数的方法会选择具体的重载并在我尝试编译时失败 - VS2015 甚至给了我 IntelliSense 告诉我我做错了™:

// compiler says yes
var foo = new[] { "cd", "ef", }.Prepend("ab");
// compiler says NO
var bar = new[] { "cd", "ef", }.Prepend();

问题是,这感觉像是黑客。我宁愿能够写出如下内容:

public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, [MinLength(1)] params T[] second)
{
    return second.Concat(first);
}

这更直观。不幸的是,C# 中的开箱即用属性不提供编译时验证,我不想在这么简单的用例中引入像 PostSharp 或 LinFu 这样的东西。

所以我的问题:有没有办法在默认的沼泽标准 C# 6 中实现这一目标?

确保 varargs 方法至少需要一个参数的最干净方法是什么

一种选择是添加 Roslyn 分析器 - 这允许您生成任意编译器错误。

另一种选择是手动添加第一个参数:

public static IEnumerable<T> Prepend<T>
  (this IEnumerable<T> @this, T first, params T[] rest)

但这很好地说明了为什么这是一个糟糕的想法 - 这意味着当您使用动态参数调用该方法时,您必须手动将数组拆分为第一个参数和其余参数。你真的想强制你的方法的所有用户检查他们传递的数组是否不为空吗?为什么?您的方法可以轻松处理这两种情况。你真的认为任意限制值得吗?没有其他 LINQ 样式的方法以这种方式工作 - 您可能会造成比解决的更多的麻烦(在我看来,您没有解决任何问题(。

目前没有比支持AOP(面向方面的编程(的工具更好的解决方案了,比如PostSharp。我会选择那个选项。

另一种选择可能是将其从代码库中提取出来,并创建一个 Roslyn 代码分析器来为你执行"质量"检查。在我看来,这不是理想的选择,但如果您不想使用 PostSharp,这可能是一种选择。