C# 铸造IEnumerable需要什么<;T>;到IEnumerator<;T>;做
我正在培训初学者一些C#的核心概念。在为随机数创建IEnumerable时,他的代码中的一个错误建议将C# 铸造IEnumerable需要什么<;T>;到IEnumerator<;T>;做,c#,.net,linq,resharper,ienumerable,C#,.net,Linq,Resharper,Ienumerable,我正在培训初学者一些C#的核心概念。在为随机数创建IEnumerable时,他的代码中的一个错误建议将IEnumerable转换为IEnumerator 我知道这两个接口都不是相互实现的,所以让我印象深刻的是ReSharper、编译器和运行时都没有抛出异常 下面是一些示例代码: 公共类SomeEnumerable:IEnumerable { 私有只读IEnumerable\u编号; 公共可枚举 { _数字=可枚举范围(0,10); } 公共IEnumerator GetEnumerator()
IEnumerable
转换为IEnumerator
我知道这两个接口都不是相互实现的,所以让我印象深刻的是ReSharper、编译器和运行时都没有抛出异常
下面是一些示例代码:
公共类SomeEnumerable:IEnumerable
{
私有只读IEnumerable\u编号;
公共可枚举
{
_数字=可枚举范围(0,10);
}
公共IEnumerator GetEnumerator()
{
返回(IEnumerator)\u编号;
}
IEnumerator IEnumerable.GetEnumerator()
{
返回GetEnumerator();
}
}
出乎意料的是,GetEnumerator
-方法编译得非常好。当初学者错误地在私有字段中输入“\u数字”时,ReSharper甚至建议添加我包含的显式演员阵容
现在考虑下面的测试代码:
var someEnumerable = new SomeEnumerable();
Console.WriteLine($"Number of elements: {someEnumerable.Count()}");
Count()
-方法调用枚举器,输出如下:
元素数:0
奇怪的是,如果您更改了类的构造函数,使其对私有字段使用IList
public SomeEnumerable()
{
_numbers = Enumerable.Range(0,10).ToArray();
}
代码抛出一个正确的InvalidCastException
我知道Enumerable.Range()
在其实现中使用了yield
语句,并且编译器可以在GetEnumerator
方法实现后立即执行一些魔术,所以这可能是一个提示
因此,我的问题是:
IEnumerable
强制转换为IEnumerator
,而不能将IList
强制转换为IEnumerator
,而不会收到警告(ReSharper)IEnumerable
强制转换为IEnumerator
。此特定对象恰好实现了两个接口。其他对象,例如您编写的SomeEnumerable
,将只实现其中一个,并且对它们进行类似的强制转换(在运行时)将失败
为什么通过Count/foreach/etc调用GetEnumerator会产生一个空结果
该对象不希望您将其强制转换为另一个类型并开始对其调用方法。您违反了该类型的约定。如果您希望它正常运行,请调用GetEnumerator
,它将为您提供一个正确的枚举数。实际上,您得到的枚举数尚未正确初始化,因为您将初始化其数据的方法
如果有一个很好的理由让它起作用,那么框架的哪一部分会利用它
它只是一种看起来像:
public class SomeClass: IEnumerable<int>, IEnumerator<int>
{
//...
}
public类SomeClass:IEnumerable,IEnumerator
{
//...
}
如果需要,您可以编写这样的类。查看
可枚举返回的类型。范围(0,10)
:
…返回
typeof(IEnumerable<Int32>)
typeof(IEnumerable)
typeof(IEnumerator<Int32>)
typeof(IDisposable)
typeof(IEnumerator)
typeof(IEnumerable)
类型(IEnumerable)
类型(IEnumerator)
类型(IDisposable)
类型(IEnumerator)
因此,您得到了它:它实现了IEnumerable
和IEnumerable
。这就是强制转换成功的原因。强制转换总是强制转换对象的实际类型,而不是其编译时类型。\u number
的编译时类型是IEnumerable
,但它的实际类型仍然是生成的类型
知道了这一点,很清楚为什么创建数组会导致无效的强制转换异常:它没有实现IEnumerator
那么为什么
new SomeEnumerable()
返回0
元素呢?我不得不在这里推测一下,但我认为这是因为状态机在这里被使用了两次,首先作为枚举器,然后作为可枚举的。在第二次使用中,它的内部指针已经在最后一次迭代中了。这段代码编译起来没有问题
using System;
using System.Collections.Generic;
public class C {
public void M() {
IEnumerable<double> x = null;
var y = (IEnumerator<double>)x;
}
}
使用系统;
使用System.Collections.Generic;
公共C类{
公屋{
IEnumerable x=null;
变量y=(IEnumerator)x;
}
}
这段代码编译起来也没有问题
public class C {
public void M() {
IMagic1<double> x = null;
var y = (IMagic2<int>)x;
}
}
public interface IMagic1<T> {
IMagic2<T> Magic();
}
public interface IMagic2<T> {
void Magic2();
}
公共C类{
公屋{
IMagic1 x=null;
变量y=(IMagic2)x;
}
}
公共接口IMagic1{
IMagic2魔术();
}
公共接口IMagic2{
无效Magic2();
}
这两种方法都是编译的,因为您告诉编译器您通过强制转换更清楚
这会产生运行时错误:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace xyz
{
public class C {
public void M() {
IMagic1<double> x = new X1();
var y = (IMagic2<int>)x;
}
}
public class X1 : IMagic1<double> {
public IMagic2<double> Magic() {
return new X2();
}
}
public class X2 : IMagic2<double> {
public void Magic2() {
}
}
public interface IMagic1<T> {
IMagic2<T> Magic();
}
public interface IMagic2<T> {
void Magic2();
}
public class Program
{
public static void Main(string[] args)
{
new C().M();
}
}
}
public class Program
{
public static void Main(string[] args)
{
var x = (IEnumerator<double>)(new double[] {0}).AsEnumerable();
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用System.Text.RegularExpressions;
名称空间xyz
{
公共C类{
公屋{
IMagic1 x=新的X1();
变量y=(IMagic2)x;
}
}
公共类X1:IMagic1{
公共图像{
返回新的X2();
}
}
公共类X2:IMagic2{
公共图书馆2(){
}
}
公共接口IMagic1{
意象2
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace xyz
{
public class C {
public void M() {
IMagic1<double> x = new X1();
var y = (IMagic2<int>)x;
}
}
public class X1 : IMagic1<double> {
public IMagic2<double> Magic() {
return new X2();
}
}
public class X2 : IMagic2<double> {
public void Magic2() {
}
}
public interface IMagic1<T> {
IMagic2<T> Magic();
}
public interface IMagic2<T> {
void Magic2();
}
public class Program
{
public static void Main(string[] args)
{
new C().M();
}
}
}
public class Program
{
public static void Main(string[] args)
{
var x = (IEnumerator<double>)(new double[] {0}).AsEnumerable();
}
}
public class Program
{
public static void Main(string[] args)
{
var x = (IEnumerator<int>)Enumerable.Range(10,1);
}
}