我刚开始学习C#,我对以下MSDN代码感到困惑:
IEnumerable<string> strings =
Enumerable.Repeat("I like programming.", 15);
由于IEnumerable是一个接口而且Enumerable.Repeat&lt;&gt;()返回IEnumerable类型,“字符串”是如何实现的?作为List还是其他容器?
答案 0 :(得分:0)
在这种情况下没有内部收藏。事实上,有些枚举可能是无限的,所以你不能总是期望内部收集。在这个特定的方法中,有一个保存值的字段和一个计算下一次移动次数的循环。如果它达到指定的计数,则停止迭代。当然,这是使用C#的迭代器功能实现的,这使得实现它变得微不足道。
答案 1 :(得分:0)
这是一个实现细节。 Enumerable<string>.Repeat()
的签名承诺结果为IEnumerable<string>
,这就是它返回的内容。
此值是以数组形式返回,List还是以任何其他方式生成(后者是这种情况),实现所需的IEnumerable<T>
无关紧要。请参阅MSDN上的备注部分,说:
此方法通过使用延迟执行来实现。立即返回值是一个对象,它存储执行操作所需的所有信息。在通过直接调用其GetEnumerator方法或在Visual C#中使用foreach或在Visual Basic中使用For Each来枚举对象之前,不会执行此方法表示的查询。
如果您对实际实施感兴趣,请参阅the proposed duplicate。
答案 2 :(得分:0)
目前无论如何,返回的类型是:
Enumerable/'<RepeatIterator>d__b5`1'<string>.
您无法将变量定义为tpye,因为它是匿名类型。匿名类型是通过编译具有名称的类型来实现的,该类型虽然有效的.NET是无效的C#,因此您不可能意外地创建具有相同名称的另一种类型。
此特定匿名类型是用于实现yield
的排序。同样,当您在C#中编写yield
时,这是通过创建实现IEnumerable
和IEnumerator
的.NET类来编译的。
您的代码并不关心任何此类代码,只关心它是否能够实现接口。
为此,请考虑以下方面的有效性:
public IEnumerable<string> SomeStrings()
{
if(new Random().Next(0, 2) == 0)
return new HashSet<string>{"a", "b", "c"};
else
return new List<string>{"a", "b", "c"};
}
调用代码无法知道它是HashSet<string>
还是List<string>
而不关心。如果版本2.0实际返回string[]
或使用yield
,则无关紧。
您可以按照以下方式创建自己的Repeat
:
public static IEnumerable<T> Repeat<T>(T element, int count)
{
while(count-- > 0)
yield return element;
}
我们有一个复杂的问题,因为如果count
小于零,我们想抛出一个异常。我们不能只做:
public static IEnumerable<T> Repeat<T>(T element, int count)
{
if(count < 0)
throw new ArgumentOutOfRangeException("count");
while(count-- != 0)
yield return element;
}
这不起作用,因为抛出不会发生,直到我们实际枚举它(如果我们做的话)为yield
- 定义的枚举在第一次枚举之前不运行任何代码。因此我们需要;
public static IEnumerable<T> Repeat<T>(T element, int count)
{
if(count < 0)
throw new ArgumentOutOfRangeException("count");
return RepeatImpl<T>(element, count);
}
private static IEnumerable<T> RepeatImpl<T>(T element, int count)
{
while(count-- != 0)
yield return element;
}