注意其本质是调用了FormattableStringFactory.Create来创建一个类型。
5. yield return,与IEnumerable<T>类型;
是“黑魔法”,但有补充说明。
yield return除了用于IEnumerable<T>以外,还可以用于IEnumerable、IEnumerator<T>、IEnumerator。
因此,如果想用C#来模拟C++/Java的generator<T>的行为,会比较简单:
var seq = GetNumbers();
seq.MoveNext();
Console.WriteLine(seq.Current); // 0
seq.MoveNext();
Console.WriteLine(seq.Current); // 1
seq.MoveNext();
Console.WriteLine(seq.Current); // 2
seq.MoveNext();
Console.WriteLine(seq.Current); // 3
seq.MoveNext();
Console.WriteLine(seq.Current); // 4
IEnumerator<int> GetNumbers()
{
for (var i = 0; i < 5; ++i)
yield return i;
}
yield return——“迭代器”发布于C# 2.0。
6. foreach循环,与IEnumerable<T>类型
是“鸭子类型”,有“操作空间”。
foreach不一定非要配合使用IEnumerable<T>类型,只要对象存在GetEnumerator()方法即可:
void Main()
{
foreach (var i in new F())
{
Console.Write(i + ", "); // 1, 2, 3, 4, 5,
}
}
class F
{
public IEnumerator<int> GetEnumerator()
{
for (var i = 0; i < 5; ++i)
{
yield return i;
}
}
}
另外,如果对象实现了GetAsyncEnumerator(),甚至也可以一样使用await foreach异步循环:
async Task Main()
{
await foreach (var i in new F())
{
Console.Write(i + ", "); // 1, 2, 3, 4, 5,
}
}
class F
{
public async IAsyncEnumerator<int> GetAsyncEnumerator()
{
for (var i = 0; i < 5; ++i)
{
await Task.Delay(1);
yield return i;
}
}
}
await foreach是C# 8.0随着异步流一起发布的,具体可见我之前写的《代码演示C#各版本新功能》。
7. using关键字,与IDisposable接口
是,也不是。
引用类型和正常的值类型用using关键字,必须基于IDisposable接口。
但ref struct和IAsyncDisposable就是另一个故事了,由于ref struct不允许随便移动,而引用类型——托管堆,会允许内存移动,所以ref struct不允许和引用类型产生任何关系,这个关系就包含继承接口——因为接口也是引用类型。
但释放资源的需求依然存在,怎么办,“鸭子类型”来了,可以手写一个Dispose()方法,不需要继承任何接口:










