C# - yield 문을 사용할 수 있는 메서드의 조건
다음과 같은 질문이 있군요. ^^
yield return의 리턴 타입 질문드립니다. 
; https://www.sysnet.pe.kr/3/0/5156
우선, yield 문을 사용할 수 있는 메서드 조건은 문서에 잘 나와 있습니다.
yield (C# Reference)
; https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/yield
- 반환 타입은 반드시 IEnumerable, IEnumerable<T>, IEnumerator, IEnumerator<T> 중의 하나여야 한다.
- 메서드의 인자에는 in, ref, out 조건을 가질 수 없다.
첫 번째 조건에 의해, C# 컴파일러는 yield 문이 사용된 메서드의 반환 형식이 IEnumerable이면 자동 생성되는 클래스를 IEnumerable과 IEnumerator를 함께 상속받도록 만듭니다. 반면 IEnumerator를 반환하는 메서드의 경우 IEnumerable은 필요 없으니 제외하고 IEnumerator만 구현합니다. 
직접 확인해 볼까요? ^^ 본문(
yield return의 리턴 타입 질문드립니다.)에 실린 예제를 보면,
using System;
using System.Collections;
using System.Collections.Generic;
public class NameSet : IEnumerable
{
    private List<string> names = new List<string>();
    public NameSet(params string[] values)
    {
        foreach (var e in values)
            this.names.Add(e);
    }
    public IEnumerable NameList()
    {
        int currentIndex = 0;
        while (currentIndex < names.Count)
        {
            yield return names[currentIndex];
            currentIndex++;
        }
    }
    public IEnumerator GetEnumerator()
    {
        int currentIndex = 0;
        while (currentIndex < names.Count)
        {
            yield return names[currentIndex];
            currentIndex++;
        }
    }
}
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var names = new NameSet("abc", "def", "ghi");
            foreach (var e in names.NameList())
                Console.WriteLine(e);
            foreach (var e in names)
                Console.WriteLine(e);
        }
    }
}
NameList 메서드의 경우 IEnumerable을 반환하므로 C# 컴파일러는 다음과 같은 식의 클래스를 생성해 줍니다.
[CompilerGenerated]
private sealed class <NameList>d__2 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IDisposable, IEnumerator
{
    // Fields
    private int <>1__state;
    private object <>2__current;
    public NameSet <>4__this;
    private int <>l__initialThreadId;
    private int <currentIndex>5__1;
    // Methods
    [DebuggerHidden]
    public <NameList>d__2(int <>1__state);
    private bool MoveNext();
    [DebuggerHidden]
    IEnumerator<object> IEnumerable<object>.GetEnumerator();
    [DebuggerHidden]
    IEnumerator IEnumerable.GetEnumerator();
    [DebuggerHidden]
    void IEnumerator.Reset();
    [DebuggerHidden]
    void IDisposable.Dispose();
    // Properties
    object IEnumerator<object>.Current { [DebuggerHidden] get; }
    object IEnumerator.Current { [DebuggerHidden] get; }
}
달리 말하면, NameList 메서드는 IEnumerable + IEnumerator가 구현된 클래스로 바뀌는 것입니다. 반면 IEnumerator를 반환하는 GetEnumerator 메서드는,
[CompilerGenerated]
private sealed class <GetEnumerator>d__3 : IEnumerator<object>, IDisposable, IEnumerator
{
    // Fields
    private int <>1__state;
    private object <>2__current;
    public NameSet <>4__this;
    private int <currentIndex>5__1;
    // Methods
    [DebuggerHidden]
    public <GetEnumerator>d__3(int <>1__state);
    private bool MoveNext();
    [DebuggerHidden]
    void IEnumerator.Reset();
    [DebuggerHidden]
    void IDisposable.Dispose();
    // Properties
    object IEnumerator<object>.Current { [DebuggerHidden] get; }
    object IEnumerator.Current { [DebuggerHidden] get; }
}
간단하게 IEnumerator만을 구현한 클래스로 바뀌는 것을 볼 수 있습니다.
참고로, foreach 구문은 IEnumerator/IEnumerable 인터페이스를 구현하지 않아도 되므로,
47. foreach 문을 사용하려면 IEnumerator/IEnumerable 인터페이스를 반드시 구현해야 할까?
; https://www.sysnet.pe.kr/2/0/11876#tag47
NameSet의 IEnumerable 상속은 (foreach에서만 사용한다면) 제거해도 무방합니다.
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]