C# 이벤트 처리 (Java 비교)
-
05-07-2019 - |
문제
현재 Delagates를 사용하여 C#에서 이벤트를 이해하고 구현하는 데 어려움을 겪고 있습니다. 나는 자바 방식에 익숙합니다.
- 여러 메소드 정의가 포함 된 리스너 유형의 인터페이스 정의
- 청취자에 정의 된 모든 이벤트에 관심이 없다면 해당 인터페이스의 어댑터 클래스를 정의하십시오.
- 이벤트를 제기하는 클래스에서 추가, 제거 및 받기 [] 메소드를 정의합니다.
- 보호 된 화재 방법을 정의하여 추가 된 청취자 목록을 통해 더러운 반복 작업을 수행하고 올바른 방법을 호출하십시오.
이것은 내가 이해하고 있습니다 (그리고 좋아요!) - C#에서 정확히 동일하게 할 수 있다는 것을 알고 있지만 C#에 대한 새로운 (더 나은?) 시스템이 마련된 것 같습니다. C#에서 대의원과 이벤트의 사용을 설명하는 수많은 튜토리얼을 읽은 후에도 여전히 무슨 일이 일어나고 있는지 이해하는 데 더 가깝지 않습니다.
요컨대, 다음 방법의 경우 C#에서 이벤트 시스템을 어떻게 구현합니까?
void computerStarted(Computer computer);
void computerStopped(Computer computer);
void computerReset(Computer computer);
void computerError(Computer computer, Exception error);
^ 위의 방법은 내가 한 번 만든 Java 응용 프로그램에서 가져온 것입니다.
많은 감사합니다!
해결책
새로운 EventArgs 기반 클래스와 함께 오류를 나타내는 네 가지 이벤트와 이벤트를 제기하는 방법을 만듭니다.
public class ExceptionEventArgs : EventArgs
{
private readonly Exception error;
public ExceptionEventArgs(Exception error)
{
this.error = error;
}
public Error
{
get { return error; }
}
}
public class Computer
{
public event EventHandler Started = delegate{};
public event EventHandler Stopped = delegate{};
public event EventHandler Reset = delegate{};
public event EventHandler<ExceptionEventArgs> Error = delegate{};
protected void OnStarted()
{
Started(this, EventArgs.Empty);
}
protected void OnStopped()
{
Stopped(this, EventArgs.Empty);
}
protected void OnReset()
{
Reset(this, EventArgs.Empty);
}
protected void OnError(Exception e)
{
Error(this, new ExceptionEventArgs(e));
}
}
그런 다음 수업은 메소드 또는 익명 기능을 사용하여 이벤트를 구독합니다.
someComputer.Started += StartEventHandler; // A method
someComputer.Stopped += delegate(object o, EventArgs e)
{
Console.WriteLine("{0} has started", o);
};
someComputer.Reset += (o, e) => Console.WriteLine("{0} has been reset");
위의 사항에 대해 주목해야 할 몇 가지 사항 :
- ONXXX 방법은 파생 클래스가 이벤트를 높일 수 있도록 보호됩니다. 이것은 항상 필요한 것은 아닙니다. 적합한대로하십시오.
- 그만큼
delegate{}
각 이벤트 선언의 조각은 널 점검을하지 않아도되는 속임수입니다. 각 이벤트에 NO-OP 이벤트 핸들러를 구독합니다. - 이벤트 선언은입니다 현장과 같은 이벤트. 실제로 만들어지는 것은 둘 다 변수입니다 그리고 이벤트. 클래스 내부에는 변수가 표시됩니다. 수업 밖에서 당신은 이벤트를 볼 수 있습니다.
내 참조 이벤트/대표 이벤트에 대한 자세한 내용은 기사입니다.
다른 팁
이를 위해 단일 대의원을 정의해야합니다
public delegate void ComputerEvent(object sender, ComputerEventArgs e);
ComputereVentargs는 다음과 같이 정의됩니다.
public class ComputerEventArgs : EventArgs
{
// TODO wrap in properties
public Computer computer;
public Exception error;
public ComputerEventArgs(Computer aComputer, Exception anError)
{
computer = aComputer;
error = anError;
}
public ComputerEventArgs(Computer aComputer) : this(aComputer, null)
{
}
}
이벤트를 해고하는 수업에는 다음과 같습니다.
public YourClass
{
...
public event ComputerEvent ComputerStarted;
public event ComputerEvent ComputerStopped;
public event ComputerEvent ComputerReset;
public event ComputerEvent ComputerError;
...
}
이 행사에 핸들러를 할당하는 방법입니다.
YourClass obj = new YourClass();
obj.ComputerStarted += new ComputerEvent(your_computer_started_handler);
당신의 핸들러는 다음과 같습니다.
private void ComputerStartedEventHandler(object sender, ComputerEventArgs e)
{
// do your thing.
}
우선, .NET에는 일반적으로 이벤트에 사용되는 표준 메소드 서명이 있습니다. 이 언어는 모든 종류의 방법 서명이 이벤트에 사용될 수있게 해주 며, 컨벤션에 결함이 있다고 믿는 일부 전문가가 있지만 (대부분 동의 함), 그것이 바로 그와 함께이를 따라갈 것입니다.
- 이벤트 매개 변수가 포함 된 클래스를 만듭니다 (Eventargs에서 파생).
public class ComputerEventArgs : EventArgs { Computer computer; // constructor, properties, etc. }
- 이벤트를 해고하는 수업에서 공개 행사를 만듭니다.
class ComputerEventGenerator // I picked a terrible name BTW. { public event EventHandler<ComputerEventArgs> ComputerStarted; public event EventHandler<ComputerEventArgs> ComputerStopped; public event EventHandler<ComputerEventArgs> ComputerReset; ... }
- 이벤트에 전화하십시오.
class ComputerEventGenerator { ... private void OnComputerStarted(Computer computer) { EventHandler<ComputerEventArgs> temp = ComputerStarted; if (temp != null) temp(this, new ComputerEventArgs(computer)); // replace "this" with null if the event is static } }
- 이벤트에 대한 핸들러를 첨부하십시오.
void OnLoad() { ComputerEventGenerator computerEventGenerator = new ComputerEventGenerator(); computerEventGenerator.ComputerStarted += new EventHandler<ComputerEventArgs>(ComputerEventGenerator_ComputerStarted); }
- 방금 첨부 한 핸들러를 만듭니다 (대부분 vs에서 탭 키를 눌러).
private void ComputerEventGenerator_ComputerStarted(object sender, ComputerEventArgs args) { if (args.Computer.Name == "HAL9000") ShutItDownNow(args.Computer); }
- 완료되면 핸들러를 분리하는 것을 잊지 마십시오. (이것을 잊어 버리는 것은 C#에서 가장 큰 메모리 누출 원입니다!)
void OnClose() { ComputerEventGenerator.ComputerStarted -= ComputerEventGenerator_ComputerStarted; }
그리고 그게 다야!
편집 : 솔직히 내 번호가 매겨진 지점이 왜 "1"으로 나타나는지 알 수 없습니다. 나는 컴퓨터가 싫어.
원하는 것을 수행하는 방법에는 여러 가지가 있습니다. 그만큼 가장 직접 방법은 호스팅 클래스의 각 이벤트에 대한 대의원을 정의하는 것입니다.
public delegate void ComputerStartedDelegate(Computer computer);
protected event ComputerStartedDelegate ComputerStarted;
public void OnComputerStarted(Computer computer)
{
if (ComputerStarted != null)
{
ComputerStarted.Invoke(computer);
}
}
protected void someMethod()
{
//...
computer.Started = true; //or whatever
OnComputerStarted(computer);
//...
}
모든 개체는이 이벤트에 대해 간단히 다음과 같이 '듣기'할 수 있습니다.
Computer comp = new Computer();
comp.ComputerStarted += new ComputerStartedDelegate(
this.ComputerStartedHandler);
protected void ComputerStartedHandler(Computer computer)
{
//do something
}
이 작업을 수행하는 '권장 표준 방법'은 컴퓨터 (및 기존/새로운 상태 및 예외) 값을 보유하기 위해 EventArgs의 서브 클래스를 정의하여 4 명의 대표단을 1로 줄이는 것입니다. 이 경우 더 깨끗한 솔루션이 될 것입니다. 나중에 확장 될 경우 컴퓨터 상태에 대한 열거가 있습니다. 그러나 기본 기술은 동일하게 유지됩니다.
- 대의원은 이벤트 핸들러/리스너의 서명/인터페이스를 정의합니다.
- 이벤트 데이터 멤버는 '청취자'목록입니다.
리스너는 += 대신 -= 구문을 사용하여 제거됩니다.
C# 이벤트는 대의원입니다. C/C ++의 함수 포인터와 비슷한 방식으로 작동하지만 System.Delegate에서 파생 된 실제 클래스입니다.
이 경우 컴퓨터 객체를 전달하기 위해 사용자 정의 EventArgs 클래스를 작성하십시오.
public class ComputerEventArgs : EventArgs
{
private Computer _computer;
public ComputerEventArgs(Computer computer) {
_computer = computer;
}
public Computer Computer { get { return _computer; } }
}
그런 다음 프로듀서로부터 이벤트를 노출시킵니다.
public class ComputerEventProducer
{
public event EventHandler<ComputerEventArgs> Started;
public event EventHandler<ComputerEventArgs> Stopped;
public event EventHandler<ComputerEventArgs> Reset;
public event EventHandler<ComputerEventArgs> Error;
/*
// Invokes the Started event */
private void OnStarted(Computer computer) {
if( Started != null ) {
Started(this, new ComputerEventArgs(computer));
}
}
// Add OnStopped, OnReset and OnError
}
이벤트 소비자는 소비자의 각 이벤트에 핸들러 기능을 바인딩합니다.
public class ComputerEventConsumer
{
public void ComputerEventConsumer(ComputerEventProducer producer) {
producer.Started += new EventHandler<ComputerEventArgs>(ComputerStarted);
// Add other event handlers
}
private void ComputerStarted(object sender, ComputerEventArgs e) {
}
}
ComputerEventProducer가 onStarted를 호출하면 시작된 이벤트가 호출되면 ComputerEventConsumer.computStarted 메소드를 호출합니다.
대의원은 함수 서명을 선언하고 클래스에서 이벤트로 사용되면 입대 한 통화 대상의 모음 역할을합니다. 이벤트의 += 및 -= 구문은 목록에 대상을 추가하는 데 사용됩니다.
이벤트로 사용 된 다음 대표들을 고려할 때 :
// arguments for events
public class ComputerEventArgs : EventArgs
{
public Computer Computer { get; set; }
}
public class ComputerErrorEventArgs : ComputerEventArgs
{
public Exception Error { get; set; }
}
// delegates for events
public delegate void ComputerEventHandler(object sender, ComputerEventArgs e);
public delegate void ComputerErrorEventHandler(object sender, ComputerErrorEventArgs e);
// component that raises events
public class Thing
{
public event ComputerEventHandler Started;
public event ComputerEventHandler Stopped;
public event ComputerEventHandler Reset;
public event ComputerErrorEventHandler Error;
}
다음과 같은 이벤트를 구독합니다.
class Program
{
static void Main(string[] args)
{
var thing = new Thing();
thing.Started += thing_Started;
}
static void thing_Started(object sender, ComputerEventArgs e)
{
throw new NotImplementedException();
}
}
인수는 무엇이든 할 수 있지만, 객체 발신자와 Eventargs E는 매우 일관되게 사용되는 협약입니다. += the_started는 먼저 대의원을 대상으로하는 대의원의 인스턴스를 생성 한 다음 이벤트에 추가합니다.
구성 요소 자체에서 일반적으로 이벤트를 발사하기 위해 메소드를 추가합니다.
public class Thing
{
public event ComputerEventHandler Started;
public void OnStarted(Computer computer)
{
if (Started != null)
Started(this, new ComputerEventArgs {Computer = computer});
}
}
대의원이 이벤트에 추가되지 않은 경우 NULL을 테스트해야합니다. 메소드를 호출 할 때 추가 된 모든 대의원이 호출됩니다. 이것이 이벤트의 경우 반환 유형이 무효 인 이유입니다. 단일 반환 값이 없으므로 정보를 피드에 넣으려면 이벤트 핸들러가 변경 될 이벤트 args에 속성이 있습니다.
또 다른 개선은 각 유형의 ARG에 대한 콘크리트 대의원을 선언하는 대신 일반 이벤트 핸들러 대의원을 사용하는 것입니다.
public class Thing
{
public event EventHandler<ComputerEventArgs> Started;
public event EventHandler<ComputerEventArgs> Stopped;
public event EventHandler<ComputerEventArgs> Reset;
public event EventHandler<ComputerErrorEventArgs> Error;
}
답변 해 주셔서 감사합니다! 마침내 나는 무슨 일이 일어나고 있는지 이해하기 시작했다. 단 하나; 각 이벤트가 다른 숫자/유형의 인수를 가지고 있다면 다른 :: eventargs 클래스를 만들어야합니다.
public void computerStarted(Computer computer);
public void computerStopped(Computer computer);
public void computerReset(Computer computer);
public void breakPointHit(Computer computer, int breakpoint);
public void computerError(Computer computer, Exception exception);
이벤트를 처리하려면 세 가지 클래스가 필요합니다!? (두 개의 사용자 정의 및 1 개는 기본 eventargs.empty 클래스를 사용하는 하나)
건배!
좋아, 최종 설명! : 그래서 이것은 해당 이벤트를 구현하기 위해 코드별로 할 수있는 최선입니까?
public class Computer {
public event EventHandler Started;
public event EventHandler Stopped;
public event EventHandler Reset;
public event EventHandler<BreakPointEvent> BreakPointHit;
public event EventHandler<ExceptionEvent> Error;
public Computer() {
Started = delegate { };
Stopped = delegate { };
Reset = delegate { };
BreakPointHit = delegate { };
Error = delegate { };
}
protected void OnStarted() {
Started(this, EventArgs.Empty);
}
protected void OnStopped() {
Stopped(this, EventArgs.Empty);
}
protected void OnReset() {
Reset(this, EventArgs.Empty);
}
protected void OnBreakPointHit(int breakPoint) {
BreakPointHit(this, new BreakPointEvent(breakPoint));
}
protected void OnError(System.Exception exception) {
Error(this, new ExceptionEvent(exception));
}
}
}