for
vs. foreach
이 두 구성이 매우 유사하고 다음과 같이 상호 교환이 가능하다는 일반적인 혼동이 있습니다.
foreach (var c in collection)
{
DoSomething(c);
}
과:
for (var i = 0; i < collection.Count; i++)
{
DoSomething(collection[i]);
}
두 키워드가 모두 같은 세 글자로 시작된다는 것은 의미 상 유사하다는 것을 의미하지는 않습니다. 이 혼란은 특히 초보자에게 오류가 발생하기 쉽습니다. 컬렉션을 반복하고 요소로 무언가를하는 것은 다음과 같이 수행됩니다 foreach
. for
당신이하고있는 일을 정말로 알지 못한다면, 이 목적을 위해 사용될 필요는 없으며 사용되어서는 안됩니다 .
예를 들어 무엇이 잘못되었는지 봅시다. 마지막으로 결과를 수집하는 데 사용되는 데모 애플리케이션의 전체 코드를 찾을 수 있습니다.
이 예에서는 "Boston"을 만나기 전에 데이터베이스에서 이름을 기준으로 Adventure Works의 도시, 더 정확하게는 일부 데이터를로드합니다. 다음과 같은 SQL 쿼리가 사용됩니다.
select distinct [City] from [Person].[Address] order by [City]
데이터는를 ListCities()
반환하는 메소드에 의해로드됩니다 IEnumerable<string>
. 다음 foreach
과 같은 모습입니다 :
foreach (var city in Program.ListCities())
{
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
for
둘 다 상호 교환 가능하다고 가정하고을 사용하여 다시 작성하십시오 .
var cities = Program.ListCities();
for (var i = 0; i < cities.Count(); i++)
{
var city = cities.ElementAt(i);
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
둘 다 같은 도시를 반환하지만 큰 차이가 있습니다.
- 사용하는 경우
foreach
, ListCities()
한 번에 전화 (47 개) 항목을 산출한다.
- 사용하는 경우
for
, ListCities()
94 회라는 전반적인 28,153 항목을 산출한다.
어떻게 된 거예요?
IEnumerable
이다 게으른 . 결과가 필요한 순간에만 작업을 수행한다는 의미입니다. 게으른 평가는 매우 유용한 개념이지만 특히 결과가 여러 번 사용되는 경우 결과가 필요한 순간을 놓치기 쉽다는 사실을 포함하여 몇 가지주의 사항이 있습니다.
의 경우 foreach
결과는 한 번만 요청됩니다. 위 for
의 잘못 작성된 코드로 구현 된의 경우 결과는 94 번 , 즉 47 × 2로 요청됩니다 .
하나 대신 데이터베이스를 94 번 쿼리하는 것은 끔찍하지만 일어날 수있는 더 나쁜 것은 아닙니다. 예를 들어, select
테이블 앞에 행을 삽입하는 쿼리가 쿼리 앞에 오는 경우 어떻게 될지 상상해보십시오 . 그래, 우리는 할 것이다 for
데이터베이스를 호출하는 2,147,483,647 는 희망 전에 충돌하지 않는 한, 번.
물론 내 코드는 편향되어 있습니다. 나는 고의적으로 게으름을 사용하고 IEnumerable
그것을 반복해서 부르는 방식으로 썼다 ListCities()
. 초보자는 절대 그렇게하지 않을 것입니다.
는 IEnumerable<T>
재산이없는 Count
,하지만 방법을 Count()
. 메소드 호출은 무섭고 결과가 캐시되지 않고 for (; ...; )
블록에 적합하지 않을 것으로 예상 할 수 있습니다 .
인덱싱을 사용할 수 없으며 LINQ 확장 방법 IEnumerable<T>
을 찾는 것이 확실하지 않습니다 ElementAt
.
아마 대부분의 초보자는 그 결과를 . ListCities()
처럼 익숙한 것으로 변환했을 것입니다 List<T>
.
var cities = Program.ListCities();
var flushedCities = cities.ToList();
for (var i = 0; i < flushedCities.Count; i++)
{
var city = flushedCities[i];
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
여전히이 코드는 다른 코드와는 매우 다릅니다 foreach
. 다시 말하지만 동일한 결과를 제공하며 이번에는 ListCities()
메소드가 한 번만 호출 되지만 575 개의 항목이 생성되고을 사용하면 foreach
47 개의 항목 만 생성됩니다.
차이는 사실에서 비롯 ToList()
됩니다 모든 데이터가 데이터베이스에서로드 할 수 있습니다. foreach
"Boston"이전의 도시 만 요청 했지만 새로운 for
도시에서는 모든 도시를 검색하여 메모리에 저장해야합니다. 575 개의 짧은 문자열을 사용하면 큰 차이가 없을 것입니다. 그러나 수십억 개의 레코드가 포함 된 테이블에서 몇 개의 행만 검색하는 경우 어떻게됩니까?
그래서 foreach
실제로 무엇입니까?
foreach
while 루프에 더 가깝습니다. 이전에 사용한 코드 :
foreach (var city in Program.ListCities())
{
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
간단히 다음으로 대체 할 수 있습니다.
using (var enumerator = Program.ListCities().GetEnumerator())
{
while (enumerator.MoveNext())
{
var city = enumerator.Current;
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
}
둘 다 동일한 IL을 생성합니다. 둘 다 같은 결과를 얻습니다. 둘 다 동일한 부작용이 있습니다. 물론 이것은 while
비슷한 무한대로 다시 작성할 수 for
있지만 더 길고 오류가 발생하기 쉽습니다. 더 읽기 쉬운 것을 자유롭게 선택할 수 있습니다.
직접 테스트하고 싶습니까? 전체 코드는 다음과 같습니다.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
public class Program
{
private static int countCalls;
private static int countYieldReturns;
public static void Main()
{
Program.DisplayStatistics("for", Program.UseFor);
Program.DisplayStatistics("for with list", Program.UseForWithList);
Program.DisplayStatistics("while", Program.UseWhile);
Program.DisplayStatistics("foreach", Program.UseForEach);
Console.WriteLine("Press any key to continue...");
Console.ReadKey(true);
}
private static void DisplayStatistics(string name, Action action)
{
Console.WriteLine("--- " + name + " ---");
Program.countCalls = 0;
Program.countYieldReturns = 0;
var measureTime = Stopwatch.StartNew();
action();
measureTime.Stop();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("The data was called {0} time(s) and yielded {1} item(s) in {2} ms.", Program.countCalls, Program.countYieldReturns, measureTime.ElapsedMilliseconds);
Console.WriteLine();
}
private static void UseFor()
{
var cities = Program.ListCities();
for (var i = 0; i < cities.Count(); i++)
{
var city = cities.ElementAt(i);
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
}
private static void UseForWithList()
{
var cities = Program.ListCities();
var flushedCities = cities.ToList();
for (var i = 0; i < flushedCities.Count; i++)
{
var city = flushedCities[i];
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
}
private static void UseForEach()
{
foreach (var city in Program.ListCities())
{
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
}
private static void UseWhile()
{
using (var enumerator = Program.ListCities().GetEnumerator())
{
while (enumerator.MoveNext())
{
var city = enumerator.Current;
Console.Write(city + " ");
if (city == "Boston")
{
break;
}
}
}
}
private static IEnumerable<string> ListCities()
{
Program.countCalls++;
using (var connection = new SqlConnection("Data Source=mframe;Initial Catalog=AdventureWorks;Integrated Security=True"))
{
connection.Open();
using (var command = new SqlCommand("select distinct [City] from [Person].[Address] order by [City]", connection))
{
using (var reader = command.ExecuteReader(CommandBehavior.SingleResult))
{
while (reader.Read())
{
Program.countYieldReturns++;
yield return reader["City"].ToString();
}
}
}
}
}
}
그리고 결과 :
--- for ---
Abingdon Albany 알렉산드리아 알함브라 [...] 본 보르도 보스턴
데이터는 94 회 호출되었고 28153 개의 아이템이 산출되었다.
--- 목록과 함께 ---
Abingdon Albany Alexandria Alhambra [...] 본 보르도 보스턴
데이터는 1 회 호출되었고 575 개의 아이템이 산출되었다.
--- 동안 ---
애 빙던 알바니 알렉산드리아 알함브라 [...] 본 보르도 보스턴
데이터를 1 회 호출하고 47 개의 아이템을 생성 하였다.
--- foreach ---
애 빙던 알바니 알렉산드리아 알함브라 [...] 본 보르도 보스턴
데이터를 1 회 호출하고 47 개의 아이템을 생성 하였다.
LINQ vs. 전통적인 방식
LINQ의 경우 C # FP가 아니라 Haskell과 같은 실제 FP 언어 인 FP ( function programming) 를 배우고 싶을 수도 있습니다 . 기능적 언어는 코드를 표현하고 표현하는 특정 방법을 가지고 있습니다. 어떤 상황에서는 비 기능적 패러다임보다 우월합니다.
그것은 (나열 조작에 관해서 FP가 훨씬 뛰어난 것으로 알려져 목록 일반적인 용어, 관련이없는 것으로 List<T>
). 이 사실을 감안할 때 C # 코드를 목록에 관해서보다 기능적으로 표현하는 기능은 오히려 좋은 것입니다.
확신이 없다면 이전 주제에 대한 기능적 및 비 기능적 방식으로 작성된 코드의 가독성을 비교하십시오 .