'수익률'의 적절한 사용
항복 키워드는 그 중 하나입니다 키워드 나 신비화 계속 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>();
}
}
나는 목록의 다음 항목 (또는 다음 항목 그룹)을 계산할 때 yield-return을 사용하는 경향이 있습니다.
버전 2를 사용하는 경우 반환하기 전에 전체 목록이 필요합니다. yield-return을 사용하면 반환하기 전에 다음 항목 만 있으면됩니다.
무엇은 복잡한 계산의 계산 비용을 더 큰 기간에 계산하는 데 도움이됩니다. 예를 들어, 목록이 GUI에 연결되어 있고 사용자가 마지막 페이지로 이동하지 언어 목록의 최종 항목을 계산하지 않습니다.
yield-return이 바람직한 또 다른 경우는 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의 http://www.dabeaz.com/generators/Generators. pdf . 프레젠테이션을 이해하기 위해 Python을 많이 알 필요는 없습니다. 저는 몰랐습니다. 제너레이터의 작동 방식을 설명하는 데 매우 유용합니다.
나는 이것이 오래된 질문이라는 것을 알고있다 키워드를 창의적으로 사용할 수있는 방법에 대한 한 가지 예를 제공하고 싶습니다. 나는 한 정말 이 기술의 혜택을. 바라건대이 질문을 우연히 발견 한 다른 누구에게 도움이 될 것입니다.
참고 : yield 키워드를 포함 컬렉션을 구축하는 또 다른 방법으로 생각하지에서. yield의 힘의 큰 부분은 호출 코드가 다음 값을 반복 할 때 중지 방법 또는 속성에서 실행 이 중지 되는 사실 에 있습니다. 내 예는 다음과 달라집니다.
yield 키워드 (Rob Eisenburg의 Caliburn.Micro 코 루틴 구현 과 )를 사용하면 다음과 같이 웹 서비스에 대한 호출을 표현할 수 있습니다.
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();
}
이렇게하면 BusyIndicator를 켜고 웹 서비스에서 로그인 메서드를 호출하고 IsLoggedIn 플래그를 반환 값으로 설정 한 다음 BusyIndicator를 다시 시작합니다.
작동 방식은 다음과 같습니다. IResult에는 Execute 메서드와 Completed 이벤트가 있습니다. Caliburn.Micro는 HandleButtonClick () 호출에서 IEnumerator를 가져 와서 Coroutine.BeginExecute 메서드에 전달합니다. BeginExecute 메서드는 IResults를 통해 반복을 시작합니다. 첫 번째 IResult가 반환되면 HandleButtonClick () 내에서 실행이 일시 중지되고 BeginExecute ()는 Completed 이벤트에 이벤트 처리기를 연결하고 Execute ()를 호출합니다. IResult.Execute ()는 동기 또는 비동기 작업을 수행 할 수 있으며 완료되면 Completed 이벤트를 발생시킵니다.
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; }
}
이와 같은 것을 설정하고 실행을 통해 진행 상황을 확인하는 것이 도움이 될 수 있습니다.
이것이 누군가를 도울 수 있기를 바랍니다! 저는 yield를 사용할 수있는 다양한 방법을 탐색하는 것을 정말 즐겼습니다.
수익률 반환은 수백만 개의 객체를 반복해야하는 알고리즘에 매우 강력 할 수 있습니다. 차량 공유에 대한 가능한 이동을 계산해야하는 다음 예를 고려하십시오. 먼저 가능한 여행을 생성합니다.
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
}
}
}
yield 대신 List를 사용하는 경우 1 백만 개의 개체를 메모리 (~ 190mb)에 할당해야하며이 간단한 예제를 실행하는 데 ~ 1400ms가 걸립니다. 그러나 yield를 사용하는 경우 이러한 모든 임시 개체를 메모리에 넣을 필요가 없으며 훨씬 더 빠른 알고리즘 속도를 얻을 수 있습니다.이 예제는 메모리 소비없이 실행하는 데 400ms 만 걸립니다.
두 코드는 실제로 두 가지 다른 작업을 수행합니다. 첫 번째 버전은 필요에 따라 구성원을 끌어옵니다. 두 번째 버전은 작업을 시작 하기 전에 모든 결과를 메모리에로드 합니다.
이것에 대한 정답이나 오답이 없습니다. 어느 것이 더 바람직한지는 상황에 따라 다릅니다. 예를 들어 쿼리를 완료해야하는 시간 제한이 있고 결과와 약간 복잡한 작업을 수행해야하는 경우 두 번째 버전이 더 바람직 할 수 있습니다. 그러나 특히 32 비트 모드에서이 코드를 실행하는 경우 큰 결과 집합에주의하십시오. 이 방법을 수행 할 때 OutOfMemory 예외에 여러 번 물린 적이 있습니다.
명심해야 할 핵심은 이것이다 : 차이점은 효율성에있다. 따라서 코드를 더 간단하게 만드는 방법을 사용하고 프로파일 링 후에 만 변경해야합니다.
수익에는 두 가지 큰 용도가 있습니다.
임시 컬렉션을 만들지 않고 사용자 지정 반복을 제공하는 데 도움이됩니다. (모든 데이터로드 및 루핑)
상태 저장 반복을 수행하는 데 도움이됩니다. (스트리밍)
아래는 위의 두 가지 사항을 지원하기 위해 전체 데모로 만든 간단한 비디오입니다.
http://www.youtube.com/watch?v=4fju3xcm21M
이것이 Chris Sells 가 The C # Programming Language의 이러한 명령문에 대해 말하는 것입니다 .
나는 때때로 yield return이 return과 같지 않다는 것을 잊는다. yield return 후 코드를 실행할 수 있다는 점에서. 예를 들어, 여기에서 첫 번째 반환 이후의 코드는 실행될 수 없습니다.
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이 없습니다. 어쨌든 Products가 IEnumerable을 구현하면 (보듯이 foreach 문에서 사용됨) 직접 반환합니다.
이 경우 코드 버전 2를 사용했을 것입니다. 사용 가능한 제품의 전체 목록이 있고 이것이이 메서드 호출의 "소비자"가 예상하는 것이므로 전체 정보를 호출자에게 다시 보내야합니다.
이 메서드의 호출자가 한 번에 "하나"의 정보를 필요로하고 다음 정보의 소비가 온 디맨드 기반 인 경우, 실행 명령이 호출자에게 반환되는지 확인하는 yield return을 사용하는 것이 좋습니다. 정보 단위를 사용할 수 있습니다.
수익률을 사용할 수있는 몇 가지 예는 다음과 같습니다.
- 호출자가 한 번에 한 단계의 데이터를 기다리는 복잡한 단계별 계산
- GUI에서 페이징-사용자가 마지막 페이지에 도달 할 수없고 현재 페이지에 정보의 하위 집합 만 공개하면되는 경우
질문에 답하기 위해 버전 2를 사용했을 것입니다.
목록을 직접 반환하십시오. 혜택:
- 더 명확 해
-
목록은 재사용이 가능합니다. (반복자가 아닙니다)실제로 사실이 아닙니다.Jon 감사합니다.
목록의 끝까지 반복 할 필요가 없다고 생각하거나 끝이 없을 때 반복기 (yield)를 사용해야합니다. 예를 들어, 클라이언트 호출은 일부 술어를 충족하는 첫 번째 제품을 검색 할 것입니다. 이는 인위적인 예이지만 반복자를 사용하는 것을 고려할 수 있으며이를 수행하는 더 좋은 방법이있을 수 있습니다. 기본적으로 전체 목록을 계산해야 함을 미리 알고 있다면 미리 수행하십시오. 그렇지 않다고 생각되면 반복자 버전 사용을 고려하십시오.
yield return keyphrase는 특정 컬렉션에 대한 상태 시스템을 유지하는 데 사용됩니다. CLR에서 사용중인 yield return keyphrase를 볼 때마다 CLR은 해당 코드 조각에 대해 열거 자 패턴을 구현합니다. 이러한 유형의 구현은 키워드가 없을 때 수행해야하는 모든 유형의 배관에서 개발자를 돕습니다.
개발자가 일부 컬렉션을 필터링하고 컬렉션을 반복 한 다음 일부 새 컬렉션에서 해당 개체를 추출한다고 가정합니다. 이런 종류의 배관은 아주 단조롭습니다.
이 기사에서 키워드 에 대해 자세히 알아보십시오 .
yield 의 사용법은 생성기 를 반환한다는 점을 제외 하면 키워드 return 과 유사합니다 . 그리고 발전기 객체는 통과합니다 번 .
수익 에는 두 가지 이점이 있습니다.
- 이 값을 두 번 읽을 필요는 없습니다.
- 많은 자식 노드를 얻을 수 있지만 모두 메모리에 넣을 필요는 없습니다.
또 다른 명확한 설명이 도움이 될 수 있습니다.
참고 URL : https://stackoverflow.com/questions/410026/proper-use-of-yield-return
'ProgramingTip' 카테고리의 다른 글
값으로 선택 옵션 '선택됨'설정 (0) | 2020.09.28 |
---|---|
Xcode 프로젝트에 대한 Git 무시 파일 (0) | 2020.09.28 |
git 커밋을 삭제하지만 변경 사항을 사용할 수 있습니까? (0) | 2020.09.28 |
클래스가있는 첫 번째 요소에 대한 CSS 선택기 (0) | 2020.09.28 |
출력을 파일 및 stdout으로 리디렉션하는 방법 (0) | 2020.09.28 |