创建扩展方法以将类型包装为 IEnumerables
Creating an extension method to wrap types as IEnumerables
我想创建一个扩展方法来有效地将单个对象包装为 IEnumerables。这是为了避免最终将 new [] {}
放在表达式中间的情况。使用以下方法很容易做到这一点:
public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this TSource source)
{
return new[] { source };
}
问题是这将应用于任何和所有类型(这是预期的行为),但这也会产生使该方法在 IEnumerable <T>
实例上可用的副作用。在解析的扩展类型是 IEnumerable<T>
的情况下,我只想 return 这个 IEnumerable,因为替代方案是发现自己有一个 IEnumerable<IEnumerable<T>>
,这不是你真正想要的d expect 调用方法时。
本能地(也许是困倦),我首先创建了一个看起来像这样的重载
public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this IEnumerable<TSource> source)
{
return source;
}
为了处理要包装的类型是 IEnumerable<T>
的情况,但是当然,控制流总是解析到第一个方法。
所以,问题是:我如何创建这样一个包装方法来处理扩展参数实例是 IEnumerable<T>
和不是 IEnumerable<T>
的情况?
这是另一个尝试,灵感来自埃里克·利珀特 (Eric Lippert) 出色的 post,网址为:。
您可以通过将 2 个扩展方法放在命名空间层次结构中的不同级别来控制重载分辨率。
namespace MyExtensions
{
public static class HighPrecendenceExtensions
{
public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this IEnumerable<TSource> source)
{
return source;
}
}
namespace LowerPrecedenceNamespace
{
public static class LowPrecedenceExtensions
{
public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this TSource source)
{
return new[] { source };
}
}
}
}
但是,缺点是您总是需要引用两个名称空间才能获得正确的方法调用行为。
using MyExtensions;
using MyExtensions.LowerPrecedenceNamespace;
你有没有考虑过稍微改变一下你的签名,让代码更复杂一点,但使用起来却非常简单。
public static class Extensions
{
public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this object source)
{
var allInterfaces = source.GetType().GetInterfaces();
IEnumerable<Type> allEnumerableInterfaces = allInterfaces.Where(t => t.Name.StartsWith("IEnumerable"));
if (!allEnumerableInterfaces.Any())
return new[] { (TSource)source };
IEnumerable<Type> genericEnumerableOfTSource = allEnumerableInterfaces.Where(t => t.GenericTypeArguments.Contains(typeof(TSource)));
// we have a generic implementation
if (genericEnumerableOfTSource.Any())
{
return (IEnumerable<TSource>) source;
}
return new[] { (TSource)source }; ;
}
}
[TestFixture]
public class Tests
{
[Test]
public void should_return_string_an_enumerable()
{
const string aString = "Hello World";
var wrapped = aString.WrapAsEnumerable<string>();
wrapped.ShouldAllBeEquivalentTo(new[] {"Hello World"});
}
[Test]
public void should_return_generic_enumerable_unwrapped()
{
var aStringList = new List<string> { "Hello", "World" };
var wrapped = aStringList.WrapAsEnumerable<string>();
wrapped.ShouldAllBeEquivalentTo(new[] { "Hello", "World" });
}
[Test]
public void should_return_non_generic_enumerable_unwrapped()
{
var aStringArray = new[] {"Hello", "World"};
var wrapped = aStringArray.WrapAsEnumerable<string>();
wrapped.ShouldAllBeEquivalentTo(new[] { "Hello", "World" });
}
}
我想创建一个扩展方法来有效地将单个对象包装为 IEnumerables。这是为了避免最终将 new [] {}
放在表达式中间的情况。使用以下方法很容易做到这一点:
public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this TSource source)
{
return new[] { source };
}
问题是这将应用于任何和所有类型(这是预期的行为),但这也会产生使该方法在 IEnumerable <T>
实例上可用的副作用。在解析的扩展类型是 IEnumerable<T>
的情况下,我只想 return 这个 IEnumerable,因为替代方案是发现自己有一个 IEnumerable<IEnumerable<T>>
,这不是你真正想要的d expect 调用方法时。
本能地(也许是困倦),我首先创建了一个看起来像这样的重载
public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this IEnumerable<TSource> source)
{
return source;
}
为了处理要包装的类型是 IEnumerable<T>
的情况,但是当然,控制流总是解析到第一个方法。
所以,问题是:我如何创建这样一个包装方法来处理扩展参数实例是 IEnumerable<T>
和不是 IEnumerable<T>
的情况?
这是另一个尝试,灵感来自埃里克·利珀特 (Eric Lippert) 出色的 post,网址为:。
您可以通过将 2 个扩展方法放在命名空间层次结构中的不同级别来控制重载分辨率。
namespace MyExtensions
{
public static class HighPrecendenceExtensions
{
public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this IEnumerable<TSource> source)
{
return source;
}
}
namespace LowerPrecedenceNamespace
{
public static class LowPrecedenceExtensions
{
public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this TSource source)
{
return new[] { source };
}
}
}
}
但是,缺点是您总是需要引用两个名称空间才能获得正确的方法调用行为。
using MyExtensions;
using MyExtensions.LowerPrecedenceNamespace;
你有没有考虑过稍微改变一下你的签名,让代码更复杂一点,但使用起来却非常简单。
public static class Extensions
{
public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this object source)
{
var allInterfaces = source.GetType().GetInterfaces();
IEnumerable<Type> allEnumerableInterfaces = allInterfaces.Where(t => t.Name.StartsWith("IEnumerable"));
if (!allEnumerableInterfaces.Any())
return new[] { (TSource)source };
IEnumerable<Type> genericEnumerableOfTSource = allEnumerableInterfaces.Where(t => t.GenericTypeArguments.Contains(typeof(TSource)));
// we have a generic implementation
if (genericEnumerableOfTSource.Any())
{
return (IEnumerable<TSource>) source;
}
return new[] { (TSource)source }; ;
}
}
[TestFixture]
public class Tests
{
[Test]
public void should_return_string_an_enumerable()
{
const string aString = "Hello World";
var wrapped = aString.WrapAsEnumerable<string>();
wrapped.ShouldAllBeEquivalentTo(new[] {"Hello World"});
}
[Test]
public void should_return_generic_enumerable_unwrapped()
{
var aStringList = new List<string> { "Hello", "World" };
var wrapped = aStringList.WrapAsEnumerable<string>();
wrapped.ShouldAllBeEquivalentTo(new[] { "Hello", "World" });
}
[Test]
public void should_return_non_generic_enumerable_unwrapped()
{
var aStringArray = new[] {"Hello", "World"};
var wrapped = aStringArray.WrapAsEnumerable<string>();
wrapped.ShouldAllBeEquivalentTo(new[] { "Hello", "World" });
}
}