C #에서 대의원 및 이벤트 (2 개) - 이벤트

이벤트에 대한

그리고 본원이 여전히 반 요약 번역 반 길에.

이 행사는 또한이다 런타임에 바인딩 메커니즘 및위원회의 설립의 지원을 기반으로합니다. 이벤트는 방송 오브젝트 (시스템 모든 이벤트에 대한 관심의 구성 요소) 무슨 일이 일어나고 있는지의 방법입니다. 다른 구성 요소는이 이벤트에 가입 할 수 있으며, 이벤트가 발생할 때 알림을받을.

예를 들어, 많은 시스템은 같은 마우스를 이동로, 이벤트를 사용자의 행동을보고 그래픽 모델이 버튼 등을 누릅니다.

할 이벤트를 구독 두 개체 (이벤트 소스와 이벤트 가입자) 간의 연결을 설정 .

이벤트 소스, 이벤트 가입자, 이벤트 소스 구성 요소 : 우리가 먼저 용어의 수를 결정하자. 아래와 같이 이들의 관계는 다음과 같습니다

이벤트 원본이 사용하는 event트리거 이벤트에 사용되는 정의 된 이벤트 키워드를.

이벤트 소스 구성 요소를 충족시킬 수있는 이벤트를 발생시킨 조건, 당신은 이벤트 소스가 이벤트를 트리거 호출 할 수있는 이벤트 소스 정의 된 장소, 일반적으로 클래스입니다.

이벤트 가입자 방법은 트리거 이벤트 후, 특정 실행 로직을 포함이라고합니다.

이벤트 설계 목표

  • 이벤트 소스와 이벤트 가입자 사이의 커플 링의 작은 정도.
  • 이벤트에 가입, 단순히 취소.
  • 이벤트 원본은 가입자가 될 수있는 여러 이벤트를 구독.

이벤트 언어 지원

정의 이벤트는, 이벤트에 가입, 탈퇴 이벤트 구문위원회 구문에 대한 확장입니다.

사용하여 이벤트를 정의 event키워드 :

public event EventHandler<FileListArgs> Progress;

이벤트 유형은 EventHandler<FileListArgs>대리자 형식이어야합니다.

정의 이벤트가 관례의 많은 따라 같은 값을 반환하지 않아도 이벤트 위임의 형식으로를, 이벤트 이름은 동사 나 문장의 동사, 과거에 입력 한 보고서는 무슨 일이 일어 났는지 사용을해야 무엇을 현재 시제 임박한 보고서의 사용.

이벤트가 발생할 때 대리자 호출 구문을 사용하여 이벤트 핸들러를 호출하는

Progress?.Invoke(this, new FileListArgs(file));

?. 운영자는 쉽게 이벤트가 등록자 때 이벤트를 발생하지되지 않도록 할 수 있습니다.

사용하여 +=운영자의 가입 이벤트를 :

//1个委托
EventHandler<FileListArgs> onProgress = (sender, eventArgs) =>
Console.WriteLine(eventArgs.FoundFile);
//事件的注册
lister.Progress += onProgress;

당신은 의뢰, 이벤트가 등록 된 개체를 볼 수 있습니다. 처리기 일 부재는 상기 일반적 프리픽스으로 행하여 발생 On위와 같이.

사용 -=구독 취소에 연산자를 :

lister.Progress -= onProgress;

우리는 위의 내용 참조 구독하고 구독 취소 할 수있는 지역 대리자를 선언 할 수 있습니다. 당신이에 가입하는 람다 식을 사용하는 경우, 당신은 가입자를 삭제할 수 없습니다.

표준 .NET 이벤트 모델

.NET 이벤트는 일반적으로 몇 가지 알려진 패턴을 따릅니다.

이벤트 위임 서명

다음과 같이 이벤트에 대한 표준 위임 서명은 다음과 같습니다 :

//委托的返回值是void,参数是object sender, EventArgs args
void OnEventRaised(object sender, EventArgs args);

반환 유형은 단독 반환 값은 모호한 것이기 때문에, 하나의 메소드의 반환 값은 하나 이상의 이벤트 가입자에게 확장 할 수 없습니다, 무효입니다.

이벤트 소스 구성 요소 및 이벤트 매개 변수 : 매개 변수 목록은 두 개의 매개 변수가 포함되어 있습니다. 로 컴파일 시간 형 이벤트 소스 구성 요소 System.Object이벤트 매개 변수는 보통 에서 유래 System.EventArgs당신은 특별한 값을 사용할 수, EventArgs.Empty기타 정보를 포함하지 않는 이벤트를 나타냅니다.

우리는 클래스 생성 다음으로 FileSearcher, 모두 트리거 이벤트의 요구 사항을 충족하기 위해 각 파일의 클래스의 요구 사항을 충족하기 위해 디렉토리에있는 모든 파일을 나열 할 수 있습니다 기능의 클래스를.

먼저 원하는 파일을 찾기 위해 다음과 같은 매개 변수에 대한 이벤트를 만들 FileFoundArgs:

public class FileFoundArgs : EventArgs
{
    public string FoundFile { get; }
    public FileFoundArgs(string fileName)
    {
        FoundFile = fileName;
    }
}

데이터를 포함하고 있지만, 우리는 매개 변수를 참조로 전달 것을 의미 클래스로 설정 단지 작은 유형, 모든 이벤트 가입자가 파라미터 업데이트의 데이터를 볼 수 있지만 것을 알 수있다. 위의 예는보기는 매개 변수를 수정할 수는 없습니다.

다음 필요에 FileSearcher이벤트 문을 만들기 위해, 우리는 대리자 형식이 이미 존재하는 사용 EventHandler<T>후 직접 선언, event변수를. 일치하는 파일에 발견되었을 때 마지막으로, 우리는 이벤트를 발생.

public class FileSearcher
{
    //系统定义的委托,把它用于声明一个事件
    public event EventHandler<FileFoundArgs> FileFound;
    public void Search(string directory, string searchPattern)
    {
        //如果文件符合要求
        foreach (var file in Directory.EnumerateFiles(directory,searchPattern))
        {
            //引发事件
            FileFound?.Invoke(this, new FileFoundArgs(file));
        }
    }
}

유사한 필드 이벤트를 정의 및 모금

절에서와 같이 이벤트 클래스를 추가하는 가장 쉬운 방법은, 이벤트의 공용 필드로 선언된다 :

public event EventHandler<FileFoundArgs> FileFound;

이것은 좋은 객체 지향 설계 아니라, 공공 영역에서 성명 것 같다,하지만 컴파일러는 래퍼를 생성하지 않습니다, 단지 이벤트 개체에 액세스 할 수있는 안전한 방법입니다. 이 행사는 유일하게 사용할 수있는 조치가 / 삭제 처리기를 추가하는 필드와 비슷합니다 :

//用lambda的方式定义一个委托
EventHandler<FileFoundArgs> onFileFound = (sender, eventArgs) => {
    Console.WriteLine(eventArgs.FoundFile);
    filesFound++;
};
//fileLister是FileSearcher的一个实例
fileLister.FileFound += onFileFound;
fileLister.FileFound -= onFileFound;

가 있지만 여기, onFileFound지역 변수가 람다에 의해 정의되어 있기 때문에,하지만, 삭제가 제대로 작동하지 않습니다.

이 클래스 외부 코드 이벤트 및 작업을 수행 다른 이벤트를 발생시킬 수 없음을 언급 할만큼 가치입니다.

이벤트 가입자의 반환 값

우리는 새로운 기능을 고려해 취소했다.

다음 장면이 설정 트리거입니다 FileFound파일이 파일의 요구 사항을 충족하기 위해 지난 경우 이벤트, 확인, 이벤트 소스 구성 요소는 다음 동작을 중지해야합니다.

이벤트 처리기 (이벤트 가입자) 값을 반환하지 않기 때문에, 그래서 당신은 다른 방법으로 전송 정보가 필요합니다. 표준 모드 이벤트 사용 EventArgs(이벤트 가입자가 정보를 전달하는 데 이러한 필드를 변경할 수 있습니다) 일부 필드가 포함 개체를.

"취소"의미론에 따라 두 가지 모드로 사용될 수있다 (어느 모드에서 요구 EventArgs중 하나 bool필드).

모드 1은 작업을 취소 할 수있는 이벤트 가입자 수 있습니다. 이 모드에서 bool필드가 초기화되고 false, 모든 이벤트 가입자으로 변경할 수 있습니다 true모든 가입자는 이벤트 알림 이벤트가 발생받을 때, FileSearcher이 값을 확인하고 추가 조치를 취할 것입니다.

모델 II는 경우 모든 이벤트 가입자는 추가 조치를 취소하고자하는 FileSearcher경우에만 다음 조치를 취소 할 수 있습니다. 다음은이 모드에서 bool필드가 초기화 true에, 어떤 경우 가입자가 변경할 수 있습니다 false(다음 작업이 계속 표시). 모든 가입자는 이벤트 알림 이벤트가 발생받을 때, FileSearcher우리는이 값을 확인하고 추가 조치를 취할 것입니다. 이 모델은 추가 단계가 있습니다 : 시작 이벤트 구성 요소가 어떤 가입자가 이벤트가 수신 있는지 알 필요가있다. 등록자가없는 경우, 필드는 거짓으로 표시됩니다.

패턴에 먼저 필요의 예에서 살펴 보자 EventArgsA의 추가 bool필드 CancelRequested:

public class FileFoundArgs : EventArgs
{
    //文件名
    public string FoundFile { get; }
    //是否取消动作
    public bool CancelRequested { get; set;}
    public FileFoundArgs(string fileName)
    {
        FoundFile = fileName;
    }
}

이 필드는 자동으로 초기화되고 false그 다음 조치를 취소 할 수있는 가입자의 요청이 있는지 여부를 확인하기 위해이 이벤트를 유발 한 후 구성 요소 요구가 플래그를 확인하려면 :

public void List(string directory, string searchPattern)
{
    foreach (var file in Directory.EnumerateFiles(directory,searchPattern))
    {
        //创建参数
        var args = new FileFoundArgs(file);
        //触发事件
        FileFound?.Invoke(this, args);
        //检查结果
        if (args.CancelRequested)
            break;
    }
}

이 모델의 장점은 지금 취소 새로운 검사 항목에 대한 아니라, 새로운 검사 항목 이후 증가는 가입자가 이전에 취소하지 않아도됩니다 큰 변화가 발생하지 않는다는 것입니다. 사용자가 필드를 지원하기 위해 신규 가입자를 확인하고 싶어하지 않는 한. 이러한 매우 느슨한 결합.

우리는 이벤트를 다음 조치를 취소하는 최초의 실행 소스 구성 요소를 찾은 후, 구독자를 업데이트합니다.

//订阅者改变参数
EventHandler<FileFoundArgs> onFileFound = (sender, eventArgs) =>
{
    Console.WriteLine(eventArgs.FoundFile);
    eventArgs.CancelRequested = true;
};

또 다른 예

또 다른 종래의 방법에 이벤트를 보여줍니다 예제를 하나 더 살펴 보자. 예 우리는 모든 하위 디렉토리를 통과. 긴 작업이 될 수있는 많은 하위 디렉토리와 디렉토리에서 새 디렉토리 검색을 시작 할 때마다 이벤트가 발생,의 이벤트를 추가 할 수 있습니다. 이는 가입자가 진행, 사용자에게 보고서의 진행 상황을 추적 할 수 있습니다. 이 시간 우리는 내부 이벤트로이 이벤트를 참조하십시오. 이 방법은 EventArgs또한 설정 될 수 private있다.

먼저 새로 만들기 EventArgs새 보고서 카탈로그 및 진행에 대한 파생 클래스를.

//internal关键字表示只能在程序集中使用,程序集外部无法访问
internal class SearchDirectoryArgs : EventArgs
{
    internal string CurrentSearchDirectory { get; }
    internal int TotalDirs { get; }
    internal int CompletedDirs { get; }
    internal SearchDirectoryArgs(string dir, int totalDirs, int completedDirs)
    {
        CurrentSearchDirectory = dir;
        TotalDirs = totalDirs;
        CompletedDirs = completedDirs;
    }
}

이벤트 정의 필드 구문을 사용하는 것 외에도,이 서로 다른 구문을 시도, 당신은 또한 추가 및 제거 핸들러 명시 적으로 생성 된 속성을 사용할 수 있습니다. 이 핸들러는 추가 코드가 필요하지 않습니다, 그러나 그것은을 만드는 방법을 보여줍니다.

//事件定义
internal event EventHandler<SearchDirectoryArgs> DirectoryChanged
{
    //事件属性
    add { directoryChanged += value; }
    remove { directoryChanged -= value; }
}
//事件声明
private event EventHandler<SearchDirectoryArgs> directoryChanged;

즉, 위의 코드는 이전 암시 적으로 정상적인 상황에서, 생성 된 코드를 보았다 필드 이벤트에 대해 정의 된 컴파일러, 그것은 이전의 속성 구문을 사용하여 이벤트를 생성하는 것은 매우 유사하다.

하자의 모양 함께의 Search모든 하위 디렉토리를 순환 방법, 그리고 두 개의 이벤트를 트리거.

public void Search(string directory, string searchPattern, bool searchSubDirs)
{
    //如果需要搜索子目录
    if (searchSubDirs)
    {
        //获取所有子目录
        var allDirectories = Directory.GetDirectories(directory, "*.*", SearchOption.AllDirectories);
        //当前已完成搜索的目录
        var completedDirs = 0;
        //目录总数
        var totalDirs = allDirectories.Length + 1;
        foreach (var dir in allDirectories)
        {
            //每搜索到1个子目录,触发事件
            directoryChanged?.Invoke(this, new SearchDirectoryArgs(dir,totalDirs,completedDirs++));
            // 递归搜索子目录
            SearchDirectory(dir, searchPattern);
        }
        // 当前目录也触发事件
        directoryChanged?.Invoke(this,new SearchDirectoryArgs(directory,totalDirs,completedDirs++));
        //递归对当前目录处理
        SearchDirectory(directory, searchPattern);
    }
    else//如果不需要搜索子目录
    {
        SearchDirectory(directory, searchPattern);
    }
}
private void SearchDirectory(string directory, string searchPattern)
{
    foreach (var file in Directory.EnumerateFiles(directory,searchPattern))
    {
        var args = new FileFoundArgs(file);
        //对于符合要求每个文件,都引发事件
        FileFound?.Invoke(this, args);
        //如果订阅者需要取消,则不进行继续搜索
        if (args.CancelRequested)
            break;
    }
}  

경우 directoryChanged에는 가입자가없는, 사용 ?.Invoke()어구가 올바르게 작동 보장 할 수 있습니다.

여기에 논리적 가입자는 이벤트가 발생할 때, 인쇄 및 완전한 디렉토리가 콘솔의 진행 상황을 확인할 수 있습니다.

lister.DirectoryChanged += (sender, eventArgs) =>
{
    Console.Write($"Entering '{eventArgs.CurrentSearchDirectory}'.");
    Console.WriteLine($" {eventArgs.CompletedDirs} of {eventArgs.TotalDirs} completed...");
};

이벤트는 C#그것을 학습함으로써, 신속하게 평소 쓸 수있는 중요한 모드 C#.NET코드입니다. 이 모드는 다음의 최신 버전에서 볼 수됩니다 .NET변경 일부.

업데이트 닷넷 코어 이벤트 모드

.NET Core더 편안한 모드, EventHandler<TEventArgs>정의는 더 이상 없습니다 TEventArgs에서 수 System.EventArgs클래스를 파생 제약.

유연성과 역방향 호환 높이기 위해 System.EventArgs클래스 소개하는 방법 MemberwoseClone(), 생성 된 오브젝트의 얕은 클론,이 방법은 매우으로부터 반사 있어야 EventArgs하는 클래스가 구현하는 함수가 도출있다.

당신은 또한 수있는 SearchDirectoryArgs구조를 변경

internal struct SearchDirectoryArgs
{
    internal string CurrentSearchDirectory { get; }
    internal int TotalDirs { get; }
    internal int CompletedDirs { get; }

    internal SearchDirectoryArgs(string dir, int totalDirs, int completedDirs) : this()
    {
        CurrentSearchDirectory = dir;
        TotalDirs = totalDirs;
        CompletedDirs = completedDirs;
    }
}

당신은하지 않아야 FileFoundArgs값 타입 참조 타입 변경, 또는 이벤트 소스 구성 요소는 가입자를 수정 관찰 할 수 없습니다.

드롭 제약은 기존 코드에 영향을주지 않습니다 어떻게 수정 이전 버전과 호환에서 살펴 보자는, 어떤 과정에서 기존 이벤트 매개 변수 유형이 될 수 System.EventArgs산출했다. 이전 버전과의 호환성은에서 계속 할 수 있다는 것입니다 System.EventArgs도출 된 주요 이유 중 하나. 그래서 지금 만든 새로운 형태의 기존 코드 기반의 모든 가입자가 없습니다.

이벤트의 비동기 가입자

올바르게 이벤트 가입자 호출을 비동기 코드를 작성하는 방법 : 마지막 모드는 당신이 배울 필요가있다. 이것은 후에있을 것이다 async and await소개 기사.

에이전트 및 이벤트 구별

을위한 기반 설계 및 이벤트 기반의 디자인 사이에서 결정하는위원회에서 .Net Core초보자 플랫폼들은 일반적으로 격렬한입니다. 두 언어의 기능은 매우 유사하기 때문에. 이벤트도 언어를 구축하는위원회입니다. 그들은 일반적인 다음에있다 :

  • 메커니즘을 런타임에 바인딩합니다 (구성 요소 노하우를 실행할 때 메서드를 호출하여 전용 통신).
  • 단일 지원하고 다수의 가입자.
  • 유사한 추가하는 구문 및 제거 핸들러를 가지고있다.
  • 이벤트를 발생과 같은 구문을 사용하여 전화를 위임.
  • 지원 Invoke().?함께 사용.

이벤트에 듣기는 선택 사항입니다

사용 기능에 어떤 언어를 결정할 때 고려해야 할 가장 중요한 요소는 부착 가입자가해야하는지 여부 . 코드는 코드의 호출에 가입해야 당신은 어떤 가입자를 호출하지 않고 모든 작업을 완료 할 수 있다면, 당신은 이벤트 기반 설계를 사용한다, 코드를 대리자 기반 설계를 사용한다.

앞의 예 고려와 결합. 사용 List.Sort()비교 기능을 제공하기 위해이 요소를 정렬에 정확해야합니다. LINQ쿼리는 반환되는 요소를 결정하기 위해위원회에 제공해야합니다. 모두 주요 사용을 기반으로 설계되어야한다. (이 시도 된 매개 수단으로 대표 동등 이벤트 파라미터에있어서의 역할을하지 않는다)

위 고려 Benpian 결합의 예. Progress보고서 작업 진행률에 대한 이벤트에 관계없이 모든 가입자 여부, 작업이 계속됩니다. FileSearcher이벤트도 추가 이벤트 가입자의 경우에도 검색하고 당신이 찾고있는 모든 파일을 찾을 것입니다, 또 다른 예이다. 두 이벤트 기반 설계를 사용한다.

### 값으로 반환 수수료 필요

우리가 사용할 수 있지만 이벤트의 대리자 형식은 잘못된 반환 형식이 EventArgs매개 변수를 전달하지만, 자연 그래서 방법에서 직접 결과를 반환하는 것이 좋습니다.

이벤트 가입자가 반환 값이 때, 우리는 대리자를 기반으로 선택했다.

이벤트 가입자는 일반적으로 긴 수명주기를 가지고

이벤트 소스 구성 요소가 시간의 매우 긴 기간에 이벤트를 발생시킬 때, 이것은 약한 정당화하지만, 당신이 그것을 찾을 수 있습니다, 이벤트 기반의 디자인은 자연스러운 것입니다. 당신은 많은 시스템, 이벤트 가입에 UX 컨트롤의 예를 볼 수 있습니다, 이벤트 소스는 프로그램의 수명주기 전반에 걸쳐 이벤트를 트리거 할 수있다.

이 많은 대리인 대비의 설계 매개 변수를 기반으로, 주요 기반으로 설계, 방법은 대리인으로 사용되지 않으며, 메소드가 리턴는 더 이상 대표단 후 사용.

신중하게 선택

위의 고려 사항은 선택을 안내하는 데 도움 수있는 반면, 필수 없습니다. 그들은 늦게 체계를 바인딩 처리하기가 매우 좋다. 최고의 설계를 전달하는 정보를 선택합니다.

추천

출처www.cnblogs.com/czjk/p/12112439.html