什么是seq <t>的真实类型?

时间:2017-10-04 22:56:21

标签: f#

seq IEnumerable 接口的别名,然后您可以创建实现 IEnumerable 的对象并使用它的方法,例如:

IEnumerable<int> list = new List<int> { 1, 2, 3, 4, 5 };

并使用IEnumerable方法:

Where, Max, etc.

但是你必须实例化一个实现 IEnumerable 的对象。

但是......在F#中你可以创建一个类似的序列:

let list = seq { for i in 1..5 -> i }

Visual Studio说你的列表有 seq 类型。这是不可能的, seq 是一个接口( IEnumerable ,您无法创建接口的实例。

那么, seq 里面的魔力是什么?

在FSI中使用GetType:

let goingToSee = seq { for i in 1..5 -> i }
goingToSee.GetType();;
val goingToSee : seq<int>
val it : System.Type = FSI_0010+goingToSee@12

1 个答案:

答案 0 :(得分:2)

F#中的seq { .. }表达式更类似于C#中的迭代器方法(使用yield关键字编写)而非集合初始值设定项。与C#编译器处理迭代器类似,F#编译器将seq { .. }表达式转换为实现IEnumerable<T>的类。

编译后的类继承自GeneratedSequenceBase(参见source code),并根据您在序列表达式中编写的内容生成代码。它被编译为状态机,因此代码看起来有点难看,但是如果你使用ILSpy来看它,它看起来像这样:

internal sealed class list@6 : GeneratedSequenceBase<int> {
  public override int GenerateNext(ref IEnumerable<int> next) {
      switch (this.pc) {
          case 1: goto IL_82;
          case 2: this.i = 0; break;
          case 3: goto IL_A3;
          default: {
              this.@enum = Operators.OperatorIntrinsics.RangeInt32(1, 1, 5).GetEnumerator();
              this.pc = 1;
              break; }
    }
      if (this.@enum.MoveNext()) {
          this.i = this.@enum.Current;
          this.pc = 2;
          this.current = this.i;
          return 1;
      }
      IL_82:
        this.pc = 3;
        LanguagePrimitives.IntrinsicFunctions.Dispose<IEnumerator<int>>(this.@enum);
      this.@enum = null;
        this.pc = 3;
      IL_A3:
        this.current = 0;
        return 0;
  }
}

我不会尝试对此进行解码,但我认为pc会保留状态机的状态。根据它,它可以初始化迭代器,移动到下一个状态,或者处置可能使用的任何资源。

值得注意的是,名称list@6中的6是此生成类所来自的行号。