문제

현재 Delagates를 사용하여 C#에서 이벤트를 이해하고 구현하는 데 어려움을 겪고 있습니다. 나는 자바 방식에 익숙합니다.

  1. 여러 메소드 정의가 포함 된 리스너 유형의 인터페이스 정의
  2. 청취자에 정의 된 모든 이벤트에 관심이 없다면 해당 인터페이스의 어댑터 클래스를 정의하십시오.
  3. 이벤트를 제기하는 클래스에서 추가, 제거 및 받기 [] 메소드를 정의합니다.
  4. 보호 된 화재 방법을 정의하여 추가 된 청취자 목록을 통해 더러운 반복 작업을 수행하고 올바른 방법을 호출하십시오.

이것은 내가 이해하고 있습니다 (그리고 좋아요!) - 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.
}

주요 차이점은 C#에서 이벤트가 인터페이스 기반이 아니라는 것입니다. 대신, 이벤트 게시자는 기능 포인터로 생각할 수있는 대의원을 선언합니다 (정확히 동일하지는 않지만 :-)). 그런 다음 가입자는 이벤트 프로토 타입을 일반 메소드로 구현하고 게시자의 이벤트 핸들러 체인에 대의원의 새로운 인스턴스를 추가합니다. 자세한 내용을 읽으십시오 대표 그리고 이벤트.

C# vs. Java 이벤트의 짧은 비교도 읽을 수도 있습니다. 여기.

우선, .NET에는 일반적으로 이벤트에 사용되는 표준 메소드 서명이 있습니다. 이 언어는 모든 종류의 방법 서명이 이벤트에 사용될 수있게 해주 며, 컨벤션에 결함이 있다고 믿는 일부 전문가가 있지만 (대부분 동의 함), 그것이 바로 그와 함께이를 따라갈 것입니다.

  1. 이벤트 매개 변수가 포함 된 클래스를 만듭니다 (Eventargs에서 파생).
public class ComputerEventArgs : EventArgs 
{
  Computer computer; 
  // constructor, properties, etc.
}
  1. 이벤트를 해고하는 수업에서 공개 행사를 만듭니다.
    class ComputerEventGenerator  // I picked a terrible name BTW.
    {
      public event EventHandler<ComputerEventArgs> ComputerStarted;
      public event EventHandler<ComputerEventArgs> ComputerStopped;
      public event EventHandler<ComputerEventArgs> ComputerReset;
    ...
    }
  1. 이벤트에 전화하십시오.
    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
      }
     }
  1. 이벤트에 대한 핸들러를 첨부하십시오.
    void OnLoad()
    {
      ComputerEventGenerator computerEventGenerator = new ComputerEventGenerator();
      computerEventGenerator.ComputerStarted += new  EventHandler<ComputerEventArgs>(ComputerEventGenerator_ComputerStarted);
    }
  1. 방금 첨부 한 핸들러를 만듭니다 (대부분 vs에서 탭 키를 눌러).
    private void ComputerEventGenerator_ComputerStarted(object sender, ComputerEventArgs args)
    {
      if (args.Computer.Name == "HAL9000")
         ShutItDownNow(args.Computer);
    }
  1. 완료되면 핸들러를 분리하는 것을 잊지 마십시오. (이것을 잊어 버리는 것은 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));
        }
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top