为什么编译器无法推断自定义集合的循环变量类型?

时间:2014-02-01 10:35:10

标签: c# loops foreach type-inference

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

enter image description here

或字符串数​​组列表:

enter image description here

显然编译器推断了item的类型。但是如果我这样做的话:

 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;
        }
    }
}

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

enter image description here

然后我想也许我需要实现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);
}

但是没有改变。现在我想知道为什么编译器无法推断自定义类型的循环变量类型? Ofcourse Foo类只是一个示例,.NET Framework中有许多自定义集合。例如ControlCollection的{​​{1}}类:

当我循环使用Form控件时,我无法执行此操作:

Form

我需要指定类型或我需要使用foreach (var item in this.Controls) { item.Name = "bla bla bla.."; //error } 扩展方法:

OfType

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

4 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:2)

它推断IEnumerator。当IEnumerator迭代object时,如何知道迭代器迭代的类型?

GetEnumerator中的Foo方法更改为:

public IEnumerator<string> GetEnumerator()

..允许你期望的那种推理..因为IEnumerator<T>实际上有类型信息。

答案 2 :(得分:0)

您需要在IEnumerable<T>上添加IEnumerable而不是{{1}}。

答案 3 :(得分:0)

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

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

如果您有一个列表,其所有项目属于同一类型,您可能希望使用IEnumerable<T>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();
    }
}