我一直认为重载的方法应该始终尽可能返回相同的类型。
但是我遇到了一个我真的不知道是否可以被视为具有不同返回类型的情况。
考虑以下实现,以获取在构成排列的元素聚合到消费者需求时所采用的k
的给定集合的所有元素的排列。
public static IEnumerable<Q> GetPermutations<T, Q>(this IEnumerable<T> collection, int k, Func<IEnumerable<T>, Q> aggregator)
{
if (k <= 0)
return Enumerable.Empty<Q>();
return collection.getPermutations(k).Select(p => aggregator(p));
}
private static IEnumerable<IEnumerable<T>> getPermutations<T>(this IEnumerable<T> set, int k) =>
k == 0 ? new[] { Enumerable.Empty<T>() }
: set.SelectMany((item, index) => set.SkipAt(index).getPermutations(k - 1).Select(p => new[] { item }.Concat(p)));
private static IEnumerable<T> SkipAt<T>(this IEnumerable<T> collection, int index)
{
var counter = 0;
foreach (var item in collection)
{
if (counter != index)
{
yield return item;
}
counter++;
}
}
现在我想定义一个不带aggregator
的重载,只是将每个排列作为IEnumerable<T>
返回。
我的第一个实现如下:
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<T> collection, int k)
=> GetPermutations(collection, k, p => p);
非常简单。但后来我意识到我正在改变方法的返回类型。如果在intellisense中进行检查,则第一个返回IEnumerable<Q>
,而第二个返回IEnumerable<IEnumerable<T>>
,类型参数Q
消失。
这应该引起关注吗?它是否可以引起争议,如果可能应该避免?从技术上讲,返回类型不会改变,因为IEnumerable<IEnumerable<T>>
是IEnumerable<Q>
但是在查看代码时它仍然引起了我的注意,因为我不记得以前看过类似的东西。< / p>
我立即想到的解决方法是让aggregator
成为可选项,但这遇到了自己的问题:
public static IEnumerable<Q> GetPermutations<T, Q>(this IEnumerable<T> collection, int k, Func<IEnumerable<T>, Q> aggregator = null)
{
if (k <= 0)
return Enumerable.Empty<Q>();
var myAggregator = aggregator ?? new Func<IEnumerable<T>, Q)(p => p); //Compile time error, no implicit conversion from `IEnumerable<T>` to `Q`
return collection.getPermutations(k).SelectMany(p => new[] { myAggregator(p) });
}
请注意,由于我们的需要和Q
提供的角落案例为string
,第二个类型参数IEnumerable<char>
是必需的。例如,Func<IEnumerable<T>, T>
类型的聚合器不起作用:
var str = "1234";
var permutations = str.GetPermutations(4, p => Concat(p)); // compile time error, `string` is not a `char`.
建议的重载是否可以接受,或者是否有解决方法我不认为可以使可选参数解决方案有效?