在c#中为迭代变量赋值

本文关键字:变量 赋值 迭代 | 更新日期: 2023-09-27 18:05:49

我在c#中使用foreach的以下代码。在一个循环中,我修改了一个List<T>,在另一个循环中,修改了一个string数组。

我们不能直接给迭代变量赋值或赋null,但我们可以修改它的属性,这些修改最终反映在List中。

这基本上意味着迭代变量是对列表中元素的引用,那么为什么我们不能直接给它赋值呢?

class Program
{
    public static void Main(string[] args)
    {
        List<Student> lstStudents = Student.GetStudents();
        foreach (Student st in lstStudents)
        {
            // st is modified and the modification shows in the lstStudents
            st.RollNo = st.RollNo + 1;
            // not allowed
            st = null;
        }
        string[] names = new string[] { "me", "you", "us" };
        foreach (string str in names)
        {
            // modifying str is not allowed
            str = str + "abc";
        }
    }
}

学生班:

class Student
{
    public int RollNo { get; set; }
    public string Name { get; set; }
    public static List<Student> GetStudents()
    {
        List<Student> lstStudents = new List<Student>();
        lstStudents.Add(new Student() { RollNo = 1, Name = "Me" });
        lstStudents.Add(new Student() { RollNo = 2, Name = "You" });
        lstStudents.Add(new Student() { RollNo = 3, Name = "Us" });
        return lstStudents;
    }
}

在c#中为迭代变量赋值

foreach中的迭代变量不是"对列表中元素的引用"——它仅仅是通过GetEnumerator()在迭代器实现中从.Current {get;}获得的值——大多数通常通过IEnumerator[<T>]获得,但并非总是如此——实际上对于List<T>来说,它是List<T>.Enumerator的值。一般情况下,对迭代器变量赋值没有"意义"。考虑:

IEnumerable<int> RandomSequence(int count) {
    var rand = new Random();
    while(count-->0) yield return rand.Next();
}

这将工作相同的foreach -这将意味着什么分配给它?

因此,foreach没有提供给迭代器变量赋值的功能。

这是因为编译器用Enumerator实现了foreach。枚举数只能在封闭集合中向前移动,它们不能改变它们所迭代的集合。

这是一个引用类型,当然你可以修改它的属性,如果它们是可写的,这些更改将反映在引用引用的对象中。

引用类型的变量根据定义是对对象的引用。但这与对实际存储位置(如变量或列表项)的引用有所不同。

你可以把它想象成一个Student变量,它只包含找到原始对象的地址。通过将列表项复制到变量中,你只是复制了地址,所以现在你有两个地址,一个在列表中,一个在变量中。通过对变量重新赋值,您将在其中写入另一个地址,但这不会影响列表中的原始条目。它也不会覆盖变量前面引用的对象。

话虽如此,foreach可能不允许您重新分配迭代变量,因为它可能导致混淆/错误。当前实现的方式是,您总是知道迭代变量是当前元素。

这里有一个具体的例子可以让你更清楚。

假设您实现了这样一个IEnumerable<int>:

public static IEnumerable<int> OneToTen()
{
    for (int i = 1; i <= 10; ++i)
    {
        yield return i;
    }
}

考虑到这一点,如果在下面的循环中你可以给n赋值,这意味着什么呢?

foreach (var n in OneToTen())
{
    n = 1; // Change original "collection"? There isn't one!
}

编译器可以让你改变n而不需要改变迭代的东西,但是这会误导你——这就是为什么编译器不允许它。