给出以下扩展方法:
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);
}
但是我必须在体内决定是否要将second
和third
以及.Concat
组合起来,或者我是否要进行多次{{1}调用。无论哪种方式,都涉及数组创建和额外的方法调用,这不是最佳的。
This answer提出了一种解决问题的简单而新颖的方法 - 声明一个no-args方法重载并将其标记为.Concat
,以便生成编译错误:
Obsolete
现在使用没有参数的方法选择具体的重载并在我尝试编译时失败 - VS2015甚至给了我智能感知告诉我我做错了:
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();
}
问题是,这感觉就像是黑客。我更愿意写下以下内容:
// compiler says yes
var foo = new[] { "cd", "ef", }.Prepend("ab");
// compiler says NO
var bar = new[] { "cd", "ef", }.Prepend();
更直观。遗憾的是,C#中的开箱即用属性不提供编译时验证,而且我不想为PostSharp或LinFu提供类似的简单用例。
所以我的问题是:有没有办法在默认情况下实现这一点,bog标准,C#6?
答案 0 :(得分:3)
一种选择是添加Roslyn分析器 - 这允许您生成任意编译器错误。
另一种选择是手动添加第一个参数:
public static IEnumerable<T> Prepend<T>
(this IEnumerable<T> @this, T first, params T[] rest)
但是这很好地说明了为什么这是一个糟糕的想法 - 这意味着当你用动态参数调用方法时,你必须手动将数组拆分为第一个参数,其余部分。您是否真的想强制方法的所有用户检查他们传递的数组是否为空?为什么?您的方法可以轻松处理这两种情况。你真的认为任意限制值得吗?没有其他LINQ风格的方法可以这样工作 - 你可能会比你解决的问题更麻烦(在我看来,你并没有解决任何问题)。
答案 1 :(得分:0)
目前没有比支持AOP(面向方面编程)的工具更好的解决方案,比如PostSharp。我会选择那个。
另一种选择可能是将其从代码库中拉出来并创建一个Roslyn code analyzer来实现“质量”&#39;检查你。这在我看来并不理想,但如果你不想使用PostSharp,它可能是一个选择。