문제
이 수율 키워드 중 하나입 키워드 C#을 계속하는 어리둥절,나는 본 적이 없었다는 것을 확신 사용하고 있습니다.
다음 두 개의 코드는 원하는 이유는?
버전 1: 수익률을 사용하여 반환
public static IEnumerable<Product> GetAllProducts()
{
using (AdventureWorksEntities db = new AdventureWorksEntities())
{
var products = from product in db.Product
select product;
foreach (Product product in products)
{
yield return product;
}
}
}
버전이 2: 목록을 반환합
public static IEnumerable<Product> GetAllProducts()
{
using (AdventureWorksEntities db = new AdventureWorksEntities())
{
var products = from product in db.Product
select product;
return products.ToList<Product>();
}
}
해결책
목록에서 다음 항목 (또는 다음 항목 그룹)을 계산할 때 수율 반복을 사용하는 경향이 있습니다.
버전 2를 사용하면 돌아 오기 전에 전체 목록이 있어야합니다. 수율 반복을 사용하면 돌아 오기 전에 다음 항목 만 있으면됩니다.
무엇보다도 이것은 복잡한 계산의 계산 비용을 더 큰 시간에 걸쳐 확산시키는 데 도움이됩니다. 예를 들어, 목록이 GUI에 연결되어 있고 사용자가 마지막 페이지로 이동하지 않으면 목록의 최종 항목을 계산하지 않습니다.
Ienumerable이 무한한 세트를 나타내는 경우 수율 재현이 바람직한 또 다른 경우입니다. 소수 목록 또는 무한한 랜덤 숫자 목록을 고려하십시오. 한 번에 전체 ienumerable을 반환 할 수 없으므로 수율 반복을 사용하여 목록을 점차적으로 반환합니다.
특정 예에서는 전체 제품 목록이 있으므로 버전 2를 사용합니다.
다른 팁
임시 목록을 채우는 것은 전체 비디오를 다운로드하는 것과 같습니다. yield
그 비디오를 스트리밍하는 것과 같습니다.
사용해야 할 때 이해하기위한 개념적 예로 yield
, 방법을 가정 해 봅시다 ConsumeLoop()
반환/생성 된 품목을 처리합니다 ProduceList()
:
void ConsumeLoop() {
foreach (Consumable item in ProduceList()) // might have to wait here
item.Consume();
}
IEnumerable<Consumable> ProduceList() {
while (KeepProducing())
yield return ProduceExpensiveConsumable(); // expensive
}
없이 yield
, 전화 ProduceList()
반환하기 전에 목록을 작성해야하기 때문에 시간이 오래 걸릴 수 있습니다.
//pseudo-assembly
Produce consumable[0] // expensive operation, e.g. disk I/O
Produce consumable[1] // waiting...
Produce consumable[2] // waiting...
Produce consumable[3] // completed the consumable list
Consume consumable[0] // start consuming
Consume consumable[1]
Consume consumable[2]
Consume consumable[3]
사용 yield
, 그것은 재배치되고 일종의 "병렬로"작동합니다.
//pseudo-assembly
Produce consumable[0]
Consume consumable[0] // immediately Consume
Produce consumable[1]
Consume consumable[1] // consume next
Produce consumable[2]
Consume consumable[2] // consume next
Produce consumable[3]
Consume consumable[3] // consume next
마지막으로, 이전에 많은 사람들이 이미 제안했듯이, 이미 완료된 목록이 있기 때문에 버전 2를 사용해야합니다.
이것은 기괴한 제안처럼 보일지 모르지만 사용하는 방법을 배웠습니다. yield
Python의 발전기에 대한 프레젠테이션을 읽어 C#의 키워드 : David M. Beazley 's http://www.dabeaz.com/generators/generator.pdf. 프레젠테이션을 이해하기 위해 많은 파이썬을 알 필요가 없습니다. 발전기의 작동 방식뿐만 아니라 왜 돌봐야하는지 설명하는 데 매우 도움이되었습니다.
나는 이것이 오래된 질문이라는 것을 알고 있지만, 수율 키워드를 창의적으로 사용하는 방법에 대한 예를 제시하고 싶습니다. 나는 가지고있다 진짜 이 기술로부터 혜택을 받았습니다. 바라건대 이것은이 질문을 우연히 발견하는 다른 사람에게 도움이 될 것입니다.
참고 : 수율 키워드에 대해서는 컬렉션을 구축하는 또 다른 방법이라고 생각하지 마십시오. 수율의 힘의 큰 부분은 실행이 일시 중지 호출 코드가 다음 값을 반복 할 때까지 방법이나 속성에서. 내 예는 다음과 같습니다.
수율 키워드 사용 (Rob Eisenburg와 함께 Caliburn.micro coroutines 구현)은 다음과 같은 웹 서비스에 비동기 호출을 표현할 수 있습니다.
public IEnumerable<IResult> HandleButtonClick() {
yield return Show.Busy();
var loginCall = new LoginResult(wsClient, Username, Password);
yield return loginCall;
this.IsLoggedIn = loginCall.Success;
yield return Show.NotBusy();
}
이것이 할 일은 SusyIndicator를 켜고 웹 서비스에서 로그인 메소드를 호출하고 IslogedIn 플래그를 반환 값으로 설정 한 다음 Busy -Indicator를 다시 끕니다.
Iresult에는 실행 방법과 완료된 이벤트가 있습니다. Caliburn.MICRO는 IENUMERATOR를 호출에서 ButtonClick ()를 처리하여 Coroutine.beginexeCute 메소드로 전달합니다. Beginexecute 방법은 노출을 통해 반복을 시작합니다. 첫 번째 iResult가 반환되면 handlbuttonclick () 내부에서 실행이 일시 중지되고 BegineXecute ()는 이벤트 핸들러를 완성 된 이벤트에 첨부하고 execute ()를 호출합니다. iresult.execute ()는 동기식 또는 비동기 작업을 수행하고 완료된 이벤트를 수행 할 수 있습니다.
loginresult는 다음과 같이 보입니다.
public LoginResult : IResult {
// Constructor to set private members...
public void Execute(ActionExecutionContext context) {
wsClient.LoginCompleted += (sender, e) => {
this.Success = e.Result;
Completed(this, new ResultCompletionEventArgs());
};
wsClient.Login(username, password);
}
public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
public bool Success { get; private set; }
}
이와 같은 것을 설정하고 실행을 통해 무슨 일이 일어나고 있는지 보는 것이 도움이 될 수 있습니다.
이것이 누군가를 도울 수 있기를 바랍니다! 나는 수확량을 사용할 수있는 다양한 방법을 탐색하는 것을 정말로 즐겼습니다.
수백만 개의 객체를 반복 해야하는 알고리즘의 경우 수율 수익률이 매우 강력 할 수 있습니다. Rideshare를위한 가능한 여행을 계산 해야하는 다음 예제를 고려하십시오. 먼저 가능한 여행을 생성합니다.
static IEnumerable<Trip> CreatePossibleTrips()
{
for (int i = 0; i < 1000000; i++)
{
yield return new Trip
{
Id = i.ToString(),
Driver = new Driver { Id = i.ToString() }
};
}
}
그런 다음 각 여행을 통해 반복하십시오.
static void Main(string[] args)
{
foreach (var trip in CreatePossibleTrips(trips))
{
// possible trip is actually calculated only at this point, because of yield
if (IsTripGood(trip))
{
// match good trip
}
}
}
수율 대신 목록을 사용하는 경우 메모리에 1 백만 개 객체를 할당해야 하며이 간단한 예제는 ~ 1400ms를 실행하는 데 걸립니다. 그러나 수율을 사용하는 경우 이러한 모든 임시 객체를 메모리에 넣을 필요가 없으며 알고리즘 속도가 훨씬 빨라질 것입니다.이 예제는 메모리 소비없이 실행하는 데 ~ 400ms 만 필요합니다.
두 개의 코드가 정말 하고 다른 두 가지입니다.첫번째 버전 풀 구성원으로 당신은 그들이 필요합니다.두번째 버전을 로드하는 모든 결과로 메모리 기 당신은 시작은 아무것도 할 필요합니다.
아무 권리 또는 잘못된 응답을 이 하나입니다.하나는 것이 바람직 그냥 상황에 따라 달라집니다.예를 들어,는 경우에는 제한이 있는 시간이 있을 완료하는 쿼리고 당신이 무언가를 할 필요가 반 복잡한 결과 함께,두번째 버전이 될 수 있는 것이 바람직하다.하지만 조심 큰 결과,특히 실행하는 경우 이 코드에서 32 비트 모드이다.나에게 물린 OutOfMemory 예외를 몇 시간을 할 때 이 방법입니다.
에 이상을 유지해야 한다는 것에 마음이만:차이가 있습니다.따라서,당신은 당신이해야 중 하나는 당신의 코드 간단하고 그것을 변경한 후에만 프로파일링입니다.
수율은 두 가지 큰 용도를 가지고 있습니다
임시 컬렉션을 만들지 않고 맞춤형 반복을 제공하는 데 도움이됩니다. (모든 데이터로드 및 루핑)
상태의 반복을 수행하는 데 도움이됩니다. (스트리밍)
아래는 위의 두 지점을 지원하기 위해 전체 데모로 만든 간단한 비디오입니다.
이것이 무엇입니다 Chris는 판매합니다 그 진술에 대해 이야기합니다 C# 프로그래밍 언어;
나는 때때로 수익률 수익률이 수익률 수익률을 실행할 수 있다는 점에서 수익률 수익률이 반환과 같지 않다는 것을 잊어 버린다. 예를 들어, 여기에서 첫 번째 반환 후 코드를 실행할 수 없습니다.
int F() { return 1; return 2; // Can never be executed }
대조적으로, 여기에서 첫 번째 수익률 반환 후 코드를 실행할 수 있습니다.
IEnumerable<int> F() { yield return 1; yield return 2; // Can be executed }
이것은 종종 if 진술에 나를 물었다.
IEnumerable<int> F() { if(...) { yield return 1; } // I mean this to be the only // thing returned yield return 2; // Oops! }
이 경우, 수익률 수익률은 수익률이 도움이되는 것처럼 "최종"이 아니라는 것을 기억합니다.
제품 LINQ 클래스가 열거/반복을 위해 유사한 수익률을 사용한다고 가정하면 첫 번째 버전은 반복 할 때마다 하나의 값만 생성하기 때문에 더 효율적입니다.
두 번째 예제는 열거 자/반복기를 Tolist () 메소드를 사용하여 목록으로 변환하는 것입니다. 이는 열거 자의 모든 항목을 수동으로 반복 한 다음 평평한 목록을 반환한다는 것을 의미합니다.
이것은 요점 외에도 다소 이루어 지지만 질문에 베스트 프로이크가 태그가 지정되므로 2 센트를 던져 버릴 것입니다. 이런 유형의 것에 대해 나는 그것을 속성으로 만들기를 크게 선호합니다.
public static IEnumerable<Product> AllProducts
{
get {
using (AdventureWorksEntities db = new AdventureWorksEntities()) {
var products = from product in db.Product
select product;
return products;
}
}
}
물론 보일러 판이 조금 더 있지만이를 사용하는 코드는 훨씬 더 깨끗해 보입니다.
prices = Whatever.AllProducts.Select (product => product.price);
vs
prices = Whatever.GetAllProducts().Select (product => product.price);
메모: 나는 그들의 일을하는 데 시간이 걸리는 방법에 대해서는 이것을하지 않을 것입니다.
그리고 무엇을 이?
public static IEnumerable<Product> GetAllProducts()
{
using (AdventureWorksEntities db = new AdventureWorksEntities())
{
var products = from product in db.Product
select product;
return products.ToList();
}
}
나는 생각이 깨끗합니다.내가하지 않 VS2008 에서 확인하는 반면,하지만.어떤 경우에,제품 구현페(하는 것으로-그것은에서 사용 foreach 서),으로 다시 돌아갈 것이라 그 모습을 볼 수 있습니다.
이 경우 코드의 버전 2를 사용했을 것입니다. 사용 가능한 제품의 전체 목록이 있으며 이것이이 방법 호출의 "소비자"가 기대하는 것이므로 전체 정보를 발신자에게 다시 보내야합니다.
이 메소드의 발신자가 한 번에 "하나의"정보가 필요하고 다음 정보의 소비가 주문형 기준이면, 수율 수익률을 사용하는 것이 유리합니다. 정보 단위를 사용할 수 있습니다.
수율 수익률을 사용할 수있는 몇 가지 예는 다음과 같습니다.
- 발신자가 한 번에 단계의 데이터를 기다리는 복잡한 단계별 계산
- GUI의 페이징 - 사용자가 마지막 페이지에 도달 할 수없고 현재 페이지에 공개 해야하는 정보 만 공개해야합니다.
귀하의 질문에 답하기 위해 버전 2를 사용했을 것입니다.
목록을 직접 반환하십시오. 이익:
- 더 분명합니다
목록은 재사용 가능합니다. (반복자는 그렇지 않습니다)사실이 아닙니다. 감사합니다 Jon
목록의 끝까지 또는 끝이 없을 때 반복 할 필요가 없다고 생각할 때부터 반복자 (수율)를 사용해야합니다. 예를 들어, 클라이언트 호출은 일부 술어를 만족시키는 첫 번째 제품을 검색 할 예정이지만 반복자를 사용하는 것을 고려할 수 있지만, 이는 예를 들어 보이지만이를 달성하는 더 좋은 방법이있을 수 있습니다. 기본적으로, 전체 목록을 계산해야한다는 것을 미리 알고 있다면, 앞에서 만하십시오. 그렇지 않다고 생각되면 반복자 버전을 사용하는 것을 고려하십시오.
수율 리턴 키 프레이즈는 특정 컬렉션의 상태 머신을 유지하는 데 사용됩니다. CLR이 수율 리턴 키 프레이즈를 사용하는 것을 볼 때마다 CLR은 해당 코드에 열거적 패턴을 구현합니다. 이러한 유형의 구현은 키워드가없는 경우 다른 유형의 배관을 개발하는 데 도움이됩니다.
개발자가 일부 컬렉션을 필터링하고 컬렉션을 반복 한 다음 새로운 컬렉션에서 해당 객체를 추출한다고 가정합니다. 이런 종류의 배관은 매우 단조롭습니다.
더 많은 것에 대해 이 기사의 키워드.