【问题标题】:Why compiler can not infer loop variable type for custom collections?为什么编译器不能推断自定义集合的循环变量类型?
【发布时间】:2014-02-01 10:35:10
【问题描述】:

有时编译器无法推断变量类型。例如,如果我这样定义string 列表:

或字符串数​​组List:

显然编译器会推断项目的类型。但如果我这样做:

 public class Foo 
    : IEnumerable
{
    List<string> _fooRecords = new List<string>();

    public IEnumerator GetEnumerator()
    {
        return _fooRecords.GetEnumerator();
    }
}

或者这个:

public class Foo 
    : IEnumerable
{
    List<string> _fooRecords = new List<string>();

    public IEnumerator GetEnumerator()
    {
        foreach (var item in _fooRecords)
        {
            yield return item;
        }
    }
}

编译器无法推断变量类型:

然后我想也许我需要实现 IEnumerator 接口,我做了这样的事情:

public class FooEnumerator : IEnumerator
{
    private List<string> recordsList;
    private int index;
    private string current;
    public FooEnumerator(List<string> list)
    {
        recordsList = list;
    }

    public object Current
    {
        get { return current; }
    }

    public bool MoveNext()
    {
        if (index != recordsList.Count - 1)
        {
            current = recordsList[index];
            index++; 
            return true;
        }
        return false;
    }

    public void Reset()
    {
        index = 0;
    }
}

并像(在 Foo 类中)一样使用它:

public IEnumerator GetEnumerator()
{
     return new FooEnumerator(_fooRecords);
}

但是什么都没有改变。现在我想知道为什么编译器不能推断自定义类型的循环变量类型?当然Foo 类只是一个例子,.NET Framework 中有许多自定义集合。例如ControlCollectionForm 类:

当我循环浏览表单控件时,我不能这样做:

foreach (var item in this.Controls)
{
     item.Name = "bla bla bla.."; //error
}

我需要指定类型或者我需要使用OfType扩展方法:

foreach (Control item in this.Controls)
{
     item.Name = "bla bla bla.."; // OK!
}

foreach (var item in this.Controls.OfType<Control>())
{
     item.Name = "bla bla bla.."; // OK!
}

现在我只是想知道真正的原因是什么,编译器不够聪明?或者在循环执行之前无法确定返回类型?

【问题讨论】:

  • 您是否尝试过实现IEnumerable&lt;T&gt; 而不是IEnumerable

标签: c# loops foreach type-inference


【解决方案1】:

non-generic enumerator 可以返回任何对象(Current 字段的类型是对象),因此编译器无法推断任何内容。在 T 类型的 generic enumerator 中,推断元素类型 T。幸运的是,foreach 构造支持指定类型并且不进行任何编译时验证,因此可以轻松处理非泛型枚举数。

【讨论】:

  • 是的,我也想知道,但我认为无论如何都有类型验证。因为返回的当前类型是你提到的对象,当我做(int item in Foo)(string item in Foo) 字符串和整数时对object 有效,因此不会出现任何错误。
  • 自然有一个验证,但它只是运行时的,当你输入错误的类型时会抛出一个 InvalidCastException。这里唯一的方便是您不必自己进行铸造。
【解决方案2】:

它推断IEnumerator。当IEnumerator 用于迭代objects 时,怎么可能知道这个迭代器在迭代什么类型?

Foo 中的GetEnumerator 方法更改为:

public IEnumerator<string> GetEnumerator()

..允许您期望的那种推断.. 因为IEnumerator&lt;T&gt; 实际上有类型信息。

【讨论】:

  • 我很高兴你明白这一点.. 对我来说有点晚了。重新阅读我的答案,我感到很尴尬..但我会离开它,因为害怕在晚上的这个时间让它变得更糟。
【解决方案3】:

除了IEnumerable之外,您还需要在/上实现IEnumerable&lt;T&gt;

【讨论】:

    【解决方案4】:

    编译器在使用 var 时并不是真正“推断”类型,它实际上是查找返回值,然后选择正确的 Type 来使用。

    您面临的问题是使用非泛型 Enumerable,这是 C# 中较旧版本的集合,未指定内部项目的类型(始终使用 Object)。这就是为什么分配给var 的类型是Object,因为返回值是Object

    如果您有一个所有项目都属于同一类型的列表,您可能希望使用IEnumerable&lt;T&gt; (here),例如:

    public class Foo : IEnumerable<string>
    {
        private readonly List<string> list;
    
        public Foo()
        {
            this.list = new List<string>();
        }
    
        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator<string> GetEnumerator()
        {
            return this.list.GetEnumerator();
        }
    
        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    }
    

    【讨论】:

    • infer - deduce or conclude (something) from evidence and reasoning rather than from explicit statements。听起来像是在推断我..
    • @SimonWhitehead 对人类来说很好,是的,但对于编译器来说,一切都是已知的,所以真的没有“推断”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多