자세 한 설명 c \ # 교체 기

10116 단어
교체 기 모델 은 디자인 모델 에서 행위 모델 (behavioral pattern) 의 한 예 로 그 는 대상 간 의 통신 을 간소화 하 는 모델 이자 쉽게 이해 하고 사용 할 수 있 는 모델 이다.쉽게 말 하면 교체 기 모드 는 배열 의 모든 요 소 를 가 져 올 수 있 습 니 다. 그 유형 이 array, list, linked list 또는 다른 어떤 배열 구조 인지 신경 쓰 지 않 아 도 됩 니 다.이 점 으로 인해 데이터 처리 채널 (data pipeline) 을 매우 효율적으로 구축 할 수 있 습 니 다. 즉, 데이터 가 처리 채널 에 들 어가 일련의 변환 을 하거나 여과 한 후에 결 과 를 얻 을 수 있 습 니 다.사실 이것 이 바로 LINQ 의 핵심 모델 이다.
. NET 에서 교체 기 모드 는 IEnumerator 와 IEnumerable 및 이에 대응 하 는 범용 인터페이스 에 의 해 봉 인 됩 니 다.하나의 클래스 가 IEnumerable 인 터 페 이 스 를 실현 하면 교체 할 수 있 습 니 다.GetEnumerator 방법 을 호출 하면 IEnumerator 인터페이스의 실현 을 되 돌려 줍 니 다. 이것 이 바로 교체 기 자체 입 니 다.교체 기 는 데이터베이스 에 있 는 커서 와 유사 하 며, 그 는 데이터 시퀀스 의 위치 기록 이다.교체 기 는 앞으로 만 이동 할 수 있 고 같은 데이터 시퀀스 에서 여러 개의 교체 기 가 동시에 데 이 터 를 조작 할 수 있다.
C \ # 1 에 교체 기 에 대한 지원 이 내장 되 어 있 습 니 다. 그것 이 바로 foreach 문 입 니 다.for 순환 구문 보다 직접적 이 고 간단 한 집합 교 체 를 할 수 있 도록 컴 파일 러 는 foreach 를 컴 파일 하여 GetEnumerator 와 MoveNext 방법 과 Current 속성 을 호출 합 니 다. 대상 이 IDisposable 인 터 페 이 스 를 실현 하면 교체 가 완 료 된 후에 교체 기 를 방출 합 니 다.그러나 C \ # 1 에서 하나의 교체 기 를 실현 하 는 것 은 상대 적 으로 좀 번 거 로 운 조작 이다.C \ # 2 는 이 일 을 크게 간단 하 게 만 들 었 고 교체 기 를 실현 하 는 많은 일 을 절약 했다.
다음은 하나의 교체 기와 C \ # 2 가 교체 기 실현 에 대한 간 화 를 어떻게 실현 하 는 지 살 펴 본 다음 에 몇 개의 교체 기 가 현실 생활 에서 의 예 를 들 어 보 자.
1. C \ # 1: 교체 기의 번 거 로 움 을 수 동 으로 실현
만약 우리 가 링 버퍼 에 기반 한 새로운 집합 유형 을 실현 해 야 한다 고 가정 합 니 다.저 희 는 IEnumerable 인 터 페 이 스 를 실현 하여 사용자 가 이 집합 중의 모든 요 소 를 쉽게 이용 할 수 있 도록 할 것 입 니 다.우 리 는 다른 세부 사항 을 무시 하고 교체 기 를 어떻게 실현 하 는 지 에 만 집중 했다.집합 은 값 을 배열 에 저장 하고 집합 은 교체 의 시작 점 을 설정 할 수 있다. 예 를 들 어 집합 에 5 개의 요소 가 있다 고 가정 하면 시작 점 을 2 로 설정 할 수 있다. 그러면 교체 출력 은 2, 3, 4, 0 이 고 마지막 은 1 이다. 간단하게 보 여줄 수 있 도록 우 리 는 설정 값 과 시작 점 의 구조 함 수 를 제공 했다.우 리 는 다음 과 같은 방식 으로 집합 을 옮 겨 다 닐 수 있 게 한다.
object[] values = { "a", "b", "c", "d", "e" };
IterationSample collection = new IterationSample(values, 3);
foreach (object x in collection)
{
    Console.WriteLine(x);
}

시작 점 을 3 으로 설정 하기 때문에 집합 출력 결 과 는 d, e, a, b 및 c 입 니 다. 현재 Iteration Sample 류 의 교체 기 를 어떻게 실현 하 는 지 살 펴 보 겠 습 니 다.
class IterationSample : IEnumerable
{
    Object[] values;
    Int32 startingPoint;
    public IterationSample(Object[] values, Int32 startingPoint)
    {
        this.values = values;
        this.startingPoint = startingPoint;
    }
    public IEnumerator GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

우 리 는 아직 GetEnumerator 방법 을 실현 하지 못 했 습 니 다. 그러나 GetEnumerator 부분의 논 리 를 어떻게 씁 니까? 첫째, 커서 의 현재 상 태 를 어 딘 가 에 존재 하 는 것 입 니 다.한편, 교체 기 모드 는 한 번 에 모든 데 이 터 를 되 돌려 주 는 것 이 아니 라 클 라 이언 트 가 한 번 에 한 데이터 만 요청 하 는 것 입 니 다.이것 은 우리 가 고객 이 현재 집합 에 요청 한 기록 을 기록 해 야 한 다 는 것 을 의미한다.C \ # 2 컴 파 일 러 는 교체 기의 상태 저장 에 많은 일 을 해 주 었 습 니 다.이제 어떤 상태 와 상태 가 어디 에 존재 하 는 지 살 펴 보 겠 습 니 다. Iteration Sample 집합 에 상 태 를 저장 하여 IEnumerator 와 IEnumerable 방법 을 실현 하려 고 합 니 다.어떻게 보면 가능 할 것 같 습 니 다. 왜냐하면 데 이 터 는 정확 한 곳 에 있 고 시작 위 치 를 포함 합 니 다.우리 의 GetEnumerator 방법 은 this 만 되 돌려 줍 니 다.그러나 이 방법 은 GetEnumerator 방법 이 여러 번 호출 되면 여러 개의 독립 된 교체 기 가 되 돌아 오 는 중요 한 문제 가 있다.예 를 들 어, 우 리 는 두 개의 포 레 치 문 구 를 사용 하여 가능 한 모든 값 을 얻 을 수 있다.이 두 개의 교 체 는 서로 독립 해 야 한다.이것 은 우리 가 GetEnumerator 를 호출 할 때마다 돌아 오 는 두 개의 교체 기 대상 이 독립 해 야 한 다 는 것 을 의미한다.우 리 는 여전히 Iteration Sample 류 에서 해당 함 수 를 통 해 직접 실현 할 수 있다.그러나 우리 류 는 여러 가지 직책 을 가지 고 있 고 이 분 은 단일 직책 원칙 을 외 웠 다.따라서 교체 기 자 체 를 실현 하기 위해 다른 종 류 를 만 듭 니 다.우 리 는 C \ # 의 내부 클래스 를 사용 하여 이 논 리 를 실현 한다.코드 는 다음 과 같 습 니 다:
class IterationSampleEnumerator : IEnumerator
{
    IterationSample parent;//       #1
    Int32 position;//        #2
    internal IterationSampleEnumerator(IterationSample parent)
    {
        this.parent = parent;
        position = -1;//        0  ,             -1,         , #3
    }

public bool MoveNext()
{
    if (position != parent.values.Length) //             ,         #4
    {
        position++;
    }
    return position < parent.values.Length;
}

public object Current
{
    get
    {
        if (position == -1 || position == parent.values.Length)//                  #5
        {
            throw new InvalidOperationException();
        }
        Int32 index = position + parent.startingPoint;//              #6
        index = index % parent.values.Length;
        return parent.values[index];
    }
}

public void Reset()
{
    position = -1;//      -1  #7
}

} 간단 한 교체 기 를 실현 하려 면 이렇게 많은 코드 를 수 동 으로 써 야 합 니 다.
  • 교 체 된 원시 집합 을 기록 해 야 합 니 다 \ # 1
  • 현재 커서 위 치 를 기록 합 니 다 \ # 2
  • 요 소 를 되 돌려 줄 때 현재 커서 와 배열 이 정의 하 는 시작 위치 에 따라 배열 에 있 는 위 치 를 설정 합 니 다 \ # 6
  • 초기 화 시 현재 위 치 를 첫 번 째 요소 이전에 설정 합 니 다 \ # 3
  • 교체 기 를 처음 호출 할 때 먼저 MoveNext 를 호출 한 다음 에 Current 속성 을 호출 해 야 합 니 다.커서 가 증가 할 때 현재 위 치 를 조건 판단 합 니 다 \ # 4
  • 무 브 넥 스 트 를 처음 호출 했 을 때 되 돌 릴 요소 가 없 더 라 도 오류 가 발생 하지 않도록 합 니 다. \ # 5
  • 교체 기 를 리 셋 할 때 현재 커서 의 위 치 를 첫 번 째 요소 이전 으로 복원 합 니 다 \ # 7
  • 현재 커서 위치 와 사용자 정의 시작 위 치 를 결합 하여 정확 한 값 을 되 돌려 주 는 것 을 제외 하고 위의 코드 는 매우 직관 적 입 니 다.이제 IterationSample 클래스 의 GetEnumerator 방법 에서 우리 가 작성 한 교체 클래스 를 되 돌려 주면 됩 니 다.
    public IEnumerator GetEnumerator()
    {
        return new IterationSampleEnumerator(this);
    }

    2. C \ # 2: yield 문 구 를 통 해 교체 간소화
    C \ # 2 는 교 체 를 더욱 간단하게 만 들 었 다. 코드 의 양 을 많이 줄 였 고 코드 도 더욱 우아 하 게 만 들 었 다.다음 코드 는 재 C \ # 2 에서 GetEnumerator 방법 을 실현 하 는 전체 코드 를 보 여 줍 니 다.
    public IEnumerator GetEnumerator()
    {
        for (int index = 0; index < this.values.Length; index++)
        {
            yield return values[(index + startingPoint) % values.Length];
        }
    }

    간단 한 몇 줄 코드 만으로 도 Iteration SampleIterator 류 에 필요 한 기능 을 완전히 실현 할 수 있 습 니 다.방법 은 매우 평범 해 보이 는데, yield return 을 사용 한 것 을 제외 하고.이 문 구 는 컴 파일 러 에 게 이것 은 일반적인 방법 이 아니 라 실행 해 야 할 교체 블록 (yield block) 이 라 고 알려 줍 니 다. 그 는 IEnumerator 대상 을 되 돌려 줍 니 다. 교체 블록 을 사용 하여 교체 방법 을 실행 하고 IEnumerable 이 실현 해 야 할 유형, IEnumerator 또는 대응 하 는 범 형 을 되 돌려 줄 수 있 습 니 다.만약 에 비 일반적인 버 전의 인터페이스 가 실현 된다 면 교체 블록 이 돌아 오 는 yield type 은 Object 형식 이 고 그렇지 않 으 면 해당 하 는 일반적인 유형 으로 돌아 갑 니 다.예 를 들 어 IEnumerable 인 터 페 이 스 를 실현 하 는 방법 이 있다 면 yield 가 돌아 오 는 유형 은 String 형식 입 니 다.교체 블록 에 서 는 yield return 을 제외 하고 일반적인 return 문 구 를 사용 할 수 없습니다.블록 에 있 는 모든 yield return 문 구 는 블록의 마지막 반환 형식 과 호 환 되 는 형식 을 되 돌려 야 합 니 다.예 를 들 어 방법 정의 가 IEnumeratble 형식 으로 돌아 가 야 한다 면 yield return 1 을 사용 할 수 없습니다.강조해 야 할 것 은 교체 블록 에 대해 우리 가 쓴 방법 은 순서대로 실행 하 는 것 처럼 보이 지만 사실은 컴 파일 러 에 게 상태 기 를 만들어 달라 고 한 것 이다.이것 이 바로 C \ # 1 에서 우리 가 쓴 부분 코드 입 니 다. 호출 자 는 호출 할 때마다 하나의 값 만 되 돌려 야 하기 때문에 마지막 반환 값 을 기억 해 야 합 니 다. 집합 에서 위 치 를 기억 해 야 합 니 다.컴 파일 러 가 교체 블록 을 만 났 을 때 상태 기 를 실현 하 는 내부 클래스 를 만 들 었 습 니 다.이 종 류 는 매개 변 수 를 포함 하여 우리 교체 기의 정확 한 현재 위치 와 로 컬 변 수 를 기억 합 니 다.이 종 류 는 우리 가 이전에 손 으로 쓴 코드 와 약간 유사 하 다. 그 는 기록 해 야 할 모든 상 태 를 인 스 턴 스 변수 로 저장 했다.다음은 교체 기 를 실현 하기 위해 이 상태 기 는 순서대로 실행 해 야 하 는 조작 을 살 펴 보 자.
    초기 상태 가 필요 합 니 다.
  • 초기 상태 가 필요 합 니 다.
  • MoveNext 가 호출 되 었 을 때 그 는 GetEnumerator 방법 중의 코드 를 실행 하여 다음 되 돌아 올 데 이 터 를 준비 해 야 한다.
  • current 속성 을 호출 할 때 yielded 의 값 을 되 돌려 야 합 니 다.
  • 교체 가 언제 끝 날 지 알 아야 합 니 다.

  • 2.2 교체 기의 실행 절차
    다음 코드 는 교체 기의 실행 절 차 를 보 여 주 었 고 코드 출력 (0, 1, 2, - 1) 을 보 여 주 며 종료 합 니 다.
    class Program {
    
      static readonly String Padding = new String(' ', 30);
      static IEnumerable<int32> CreateEnumerable()
      {
          Console.WriteLine("{0} CreateEnumerable()    ", Padding);
          for (int i = 0; i &lt; 3; i++)
          {
              Console.WriteLine("{0}   yield {1}", i);
              yield return i;
              Console.WriteLine("{0}yield   ", Padding);
          }
          Console.WriteLine("{0} Yielding     ", Padding);
          yield return -1;
          Console.WriteLine("{0} CreateEnumerable()    ", Padding);
      }
    
      static void Main(string[] args)
      {
          IEnumerable<int32> iterable = CreateEnumerable();
          IEnumerator<int32> iterator = iterable.GetEnumerator();
          Console.WriteLine("    ");
          while (true)
          {
              Console.WriteLine("  MoveNext  ……");
              Boolean result = iterator.MoveNext();
              Console.WriteLine("MoveNext     {0}", result);
              if (!result)
              {
                  break;
              }
              Console.WriteLine("     ……");
              Console.WriteLine("        {0}", iterator.Current);
          }
          Console.ReadKey();
      }
    }

    출력 결과 에서 몇 가 지 를 볼 수 있 습 니 다.
  • MoveNext 를 처음 호출 할 때 까지 Create Enumerable 의 방법 이 호출 되 었 습 니 다.
  • MoveNext 를 호출 할 때 모든 작업 을 마 쳤 고 Current 속성 으로 돌아 가 코드 가 실행 되 지 않 았 습 니 다.
  • 코드 는 yield return 이후 실행 을 중단 하고 다음 MoveNext 방법 을 호출 할 때 계속 실 행 됩 니 다.
  • 방법 에 여러 개의 yield return 문 구 를 가 질 수 있다.
  • 마지막 yield return 이 실 행 된 후에 코드 가 종료 되 지 않 았 습 니 다.MoveNext 를 호출 하여 false 를 되 돌려 방법 을 끝 냅 니 다.첫 번 째 는 특히 중요 하 다. 이것 은 교체 블록 에 방법 호출 시 즉시 실행 해 야 하 는 코드 를 쓸 수 없다 는 것 을 의미한다. 예 를 들 어 매개 변수 검증 이다.만약 에 매개 변수 검증 을 교체 블록 에 넣 으 면 그 는 좋 은 역할 을 하지 못 할 것 이다. 이것 은 자주 발생 하 는 잘못된 부분 이 고 이런 오 류 는 쉽게 발견 되 지 않 을 것 이다.교체 중단 및 finally 구문 블록 의 특수 집행 방식 을 살 펴 보 겠 습 니 다.

  • 2.3 교체 기의 특수 집행 절차
    일반적인 방법 에서 return 문 구 는 보통 두 가지 역할 을 하 는데 하 나 는 호출 자가 실행 한 결 과 를 되 돌려 주 는 것 이다.둘째, 종료 방법의 집행, 종료 전에 finally 문 구 를 실행 하 는 방법 입 니 다.위의 예 에서 우 리 는 yield return 문 구 는 짧 은 종료 방법 일 뿐 MoveNext 가 다시 호출 될 때 계속 실행 하 는 것 을 보 았 다.여기 서 우 리 는 finally 문장 블록 을 쓰 지 않 았 다.어떻게 진정한 종료 방법, 종료 방법 시 finnally 구문 블록 이 어떻게 실행 되 는 지, 다음은 비교적 간단 한 구 조 를 살 펴 보 겠 습 니 다: yield break 구문 블록.yield break 를 사용 하여 교체 종료
    static IEnumerable<int32> CountWithTimeLimit(DateTime limit)
    {
        try
        {
            for (int i = 1; i &lt;= 100; i++)
            {
                if (DateTime.Now >= limit)
                {
                    yield break;
                }
                yield return i;
            }
        }
        finally
        {
            Console.WriteLine("    !"); Console.ReadKey();
        }
    }
    static void Main(string[] args)
    {
        DateTime stop = DateTime.Now.AddSeconds(2);
        foreach (Int32 i in CountWithTimeLimit(stop))
        {
            Console.WriteLine("   {0}", i);
            Thread.Sleep(300);
        }
    }

    Asp. Net 고급 기술 군 89336052

    좋은 웹페이지 즐겨찾기