最近在写代码的时候突然有个想法,写foreach的时候在想,用for代替foreach是不是除了写法差异以外完全没有什么影响?于是就很好奇,如果这两个函数效果完全一致,那为什么要保留for的写法?或者为什么会有foreach的写法出来?仅仅是为了写法简单一点吗?
带着好奇我大概测试了一些增删改的轮询情况
首先,创建一个整形数组待之后循环输出
int[] intArr = {1,2,3,4,5,6,7,8,9};
再创建一个整形List列表等待之后做输出
List<int> intList = new List<int>;
intList.Add(1);
intList.Add(2);
intList.Add(3);
intList.Add(4);
intList.Add(5);
for:使用数组:
for(var i=0 ; i<intArr.Count ; i++)
{
if(intArr[i] == 1)
{
intArr[4] = 11;
}
Console.WriteLine(intArr[i]);
}
输出:
1
2
3
4
11
6
7
8
9
foreach:使用数组:
foreach(int i in intArr)
{
if(i == 1)
{
intArr[4] = 11;
}
Console.WriteLine(i);
}
输出:集合已修改;可能无法执行枚举操作。
小结:在for循环中修改数据会输出修改后的数据,而foreach循环中如果修改了正在循环的数据,那么将会返回错误。
for:使用List删除元素:
for(int i = 0; i < intList.Count ; i++)
{
if(intList[i] == 1)
{
intList.Remove(3);
}
Console.WriteLine(intList[i]);
}
输出:
1
2
4
5
foreach:使用list删除元素:
foreach(int i in intList)
{
if(i == 1)
{
intList.Remove(3)
}
Console.WriteLine(i);
}
输出:集合已修改;可能无法执行枚举操作。
新增元素同理
小结:for循环的过程中会每次去重新计算条件语句,而foreach循环则会因为条件语句被隐藏,且条件语句发生变化而返回错误。
这时我突然想起来之前经常用DataTable来做循环,同时给DataTable中的空值做赋值,所以对上面的结论产生了质疑,似乎事情并没有那么简单,于是我想重新用DataTable来测试一下。
当使用DataTable:
System.Data.DataTable DTInfo = new System.Data.DataTable();
DTInfo.Columns.Add("IntName", typeof(int));
System.Data.DataRow dr_info = DTInfo.NewRow();
dr_info["IntName"] = 10;
DTInfo.Rows.Add(dr_info);
foreach:使用DataTable修改:
foreach(System.Data.DataRow dr in DTInfo.Rows)
{
if(Convert.ToInt32(dr["IntName"]) == 10)
{
dr["IntName"] = 11;
}
Console.WriteLine(dr["IntName"]);
}
输出:
11
foreach使用DataTable新增:
foreach(System.Data.DataRow dr in DTInfo.Rows)
{
if(Convert.ToInt32(dr["IntName"]) == 10)
{
System.Data.DataRow dr_Add = DTInfo.NewRow();
dr_Add["IntName"] = 10;
DTInfo.Rows.Add(dr_Add);
}
Console.WriteLine(dr["IntName"]);
}
输出:集合已修改;枚举操作可能无法执行。
小结:在本次测试中,foreach很自然的反馈了DataTable内部数据的改变,但是当数据量发生变化的时候却提示了错误。数据量的变化问题暂且不说,数据本身发生变化却没有返回错误,这个结论与之前的结论显然冲突了。
此时我突然想到一种可能,想要验证就做了下面这个数组
List<String>[] myList = new List<String>[3];
myList[0] = new List<string>();
myList[1] = new List<string>();
myList[2] = new List<string>();
myList[0].Add("A");
myList[0].Add("B");
myList[0].Add("C");
myList[1].Add("a");
myList[1].Add("b");
myList[1].Add("c");
myList[2].Add("1");
myList[2].Add("2");
myList[2].Add("3");
接着用foreach循环输出并试图修改其中的一个值
foreach (List<string> ls in myList)
{
if (ls[0] == "A")
{
myList[2][0] = "4";
}
Console.WriteLine("ls[0]");
}
输出:
A
a
4
至此,大概的原因应该是找到了。
结论:在C#中,数据类型大概有两种,一种是值类型,一种是引用类型。值类型的变量直接在线程栈中分配空间,而引用类型的变量会在托管堆中分配空间,在线程栈中只有一个指向托管堆位置的空箱。在这两种基本类型的基础上有一种类型叫复合类型,它是说多个值类型的复合(值复合类型)或多个引用类型的复合(引用复合类型)。
特例:下面是我再测试的过程中试出来的一些其他情况
若在foreach操作数组时将intArr转为List:
foreach(int i in intArr.ToList())
{
if(i == 1)
{
intArr[4] = 11;
}
Console.WriteLine(i);
}
输出:
1
2
3
4
5
6
7
8
9
若在for删除List元素时,删除已经循环过的元素:
for(int i = 0; i < intList.Count ; i++)
{
if(intList[i] == 4)
{
intList.Remove(3);
}
Console.WriteLine(intList[i]);
}
输出:
1
2
3
5