C# 铸造IEnumerable需要什么<;T>;到IEnumerator<;T>;做

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()

我正在培训初学者一些C#的核心概念。在为随机数创建IEnumerable时,他的代码中的一个错误建议将
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)
  • 为什么通过Count/foreach/etc调用GetEnumerator会产生一个空结果
  • 如果有一个很好的理由让它起作用,那么框架的哪一部分会利用它
  • 一些澄清: 我知道不应该这样做,我也知道这样做是行不通的。但我感兴趣的是,为什么代码会产生IL,而IL编译和执行得非常好,但会产生如此奇怪的结果

    为什么演员阵容是合法的,为什么没有警告

    强制转换的全部意义在于你告诉编译器,“我知道你不认为这个类型实际上是另一个类型,但我知道的比你多,让我把它当作是另一个类型,如果我错了,在运行时抛出一个异常

    为什么您可以将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);
        }
    }