2013-07-16 3 views
4

시나리오 : 일정한 시스템과 각 타이머 이벤트를 구축하고 있는데 일반적인 Timer.Elapsed 이벤트 대신 사용자 지정 메서드를 실행하려고합니다.왜 내 대리인은 foreach 루프의 마지막 항목 만 사용합니까?

그래서 나는 이렇게 썼습니다.

foreach (ScheduleElement schedule in schedules) { 
    TimeSpan timeToRun = CalculateTime(schedule); 
    schedule.Timer = new Timer(timeToRun.TotalMilliseconds); 
    schedule.Timer.Elapsed += delegate { Refresh_Timer(schedule); }; 
    schedule.Timer.AutoReset = true; 
    schedule.Timer.Enabled = true; 
} 

좋아요. 실제로 그렇게 간단하게 실제로 타이머를 만들었습니다. 그러나 전달 된 일정 요소를 사용하여 각 경과 이벤트를 실행하고 싶습니다. 제 질문은 Elapsed 이벤트가 모든 단일 Timer.Elapsed 이벤트에 대해 for 루프의 마지막 ScheduleElement에만 전달되는 이유입니다.

이제는 어떤 문제가 해결되었는지 알지만 이유는 확실하지 않습니다. 원래 Timer.Elapsed 이벤트로 롤백하고 자체 클래스로 Timer 클래스를 확장하면이 클래스를 해결할 수 있습니다. 그렇게.

수정은 :

foreach (ScheduleElement schedule in schedules) { 
    TimeSpan timeToRun = CalculateTime(schedule); 
    schedule.Timer = new TimerEx(timeToRun.TotalMilliseconds); 
    schedule.Timer.Elapsed +=new System.Timers.ElapsedEventHandler(Refresh_Timer); 
    schedule.Timer.Tag = schedule; 
    schedule.Timer.AutoReset = true; 
    schedule.Timer.Enabled = true; 
} 

나는 다시 원래의 객체로 object sender 캐스팅하고, 각각의 고유 한 타이머 나에게 내 정확한 일정을 제공합니다 그것의 오프 Tag 속성을 훔치다.

다시 말해서 delegate { }은 foreach 루프의 마지막 타이머 ScheduleElement만을 모든 타이머에 전달하는 이유는 무엇입니까?

EDIT 1

Timer 클래스

public TimerEx : Timer { 

    public TimerEx(double interval) : base(interval) { } 

    private Object _Tag; 

    public Object Tag { 
     get { return _Tag; } 
     set { _Tag = value; } 
    } 
} 
+1

연구 "루프 이상 종료" – Matthew

+0

확인 : http://stackoverflow.com/questions/271440/c-sharp-captured-variable-in-loop – fcuesta

답변

9

당신이 당신의 위임에 폐쇄를 사용하고 있기 때문이고, 그것은 각각의 반복에 대해 공유하는 같은 변수를 통해 닫습니다 foreach 루프.

자세한 내용은 Eric Lippert의 기사 Closing over the loop variable considered harmful을 참조하십시오. 이 경우

, 당신은 쉽게 임시로 문제를 해결할 수 있습니다

foreach (ScheduleElement schedule in schedules) { 
    TimeSpan timeToRun = CalculateTime(schedule); 
    schedule.Timer = new Timer(timeToRun.TotalMilliseconds); 

    // Make a temporary variable in the proper scope, and close over it instead 
    var temp = schedule; 
    schedule.Timer.Elapsed += delegate { Refresh_Timer(temp); }; 

foreach 루프에 대한 C# 5 변경이 문제가. 최신 컴파일러로 이것을 컴파일하면 더 이상 문제가 존재하지 않습니다.

+0

최신 컴파일러 사용에 대한 자세한 내용은 어디에서 확인할 수 있습니까? ? VS가 사용하고있는 것이 무엇인지 잘 모르겠습니다. 2010 년입니다. – meanbunny

+1

@ meanbunny 새로운 동작을 얻으려면 VS 2012 이상 버전을 사용해야합니다. 그것은 VS 2012와 함께 릴리스 된 C# 5에서 변경되었습니다. –

+0

그래, 고마워, 좋은 대답과 정말 고마워! 받아 들일 때까지 4 분 남았어요. – meanbunny

관련 문제