In C # delegates and events (two) - event

About the event

And on an as herein, this still semi summary translation half way.

The event is also a late-binding mechanism, and is based on the support of the establishment of the commission. Events are a way of what is happening broadcast objects (components of interest to the system all the events). Any other components can subscribe to this event, and to be notified when the event occurs.

For example, many systems have a graphical model to report the event the user action, such as moving the mouse, press the button and so on.

Subscribe to the event will establish a coupling between two objects (event source and event subscribers) .

Let us first determine the number of terms: event source, event subscriber, event source components. Their relationship is as shown below:

Event Source is to use the eventevent keyword defined, which is used to trigger events.

Event Source component is the event source defined place, usually a class, when the conditions that caused the event to meet, you can call the event source triggered the event.

Event subscriber method is called after the trigger event, contain a specific execution logic.

Event design goals

  • Small degree of coupling between the event source and event subscribers.
  • Subscribe to an event, simply unsubscribe.
  • Event Source subscribe to multiple events can be subscribers.

Event language support

Defined events, subscribing to events, unsubscribe event syntax is an extension to the commission syntax.

Define an event using eventthe keywords:

public event EventHandler<FileListArgs> Progress;

Event type EventHandler<FileListArgs>must be a delegate type.

Defined events to follow many of the conventions , such as the type of event delegate need not return a value, the event name should be a verb or verb phrase, use what type reports in the past has happened, what the use of the present tense impending report.

When the event is raised, using the delegate call syntax to invoke event handlers

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

?. Operators can easily ensure that the event is not raised when an event no subscribers.

By using +=operator subscription events:

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

You can see, the event is registered objects commissioned. Handler occurs something member further subjected, usually with the prefix On, as shown above.

Use -=operators to unsubscribe:

lister.Progress -= onProgress;

We can see above declare a local delegate to subscribe and unsubscribe. If you use a lambda expression to subscribe to, then you will not be able to delete the subscribers.

Standard .NET event model

.NET events usually follow some known patterns.

Event delegate signature

Standard delegate signature for the event are as follows:

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

Return type is void, because the return value alone would be ambiguous, a single method's return value can not be extended to more than one event subscribers.

Parameter list contains two parameters: Event Source Components and event parameters. Compile-time type event source component to System.Objectthe event parameter is usually derived from System.EventArgs, you can use a special value EventArgs.Emptyto represent the event does not contain any other information.

Next we create a class FileSearcher, the class of functions that can list all files in a directory to meet the requirements of the class for each file to meet the requirements of both a trigger event.

First create an event for the following parameters to find the desired file FileFoundArgs:

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

It can be seen that although only a small type that contains data, but we set it to the class, which means that the parameters passed by reference, all event subscribers will see the data of the parameter update. The above example can only view can not modify the parameters.

In the next need FileSearcherto create an event statement, we use the delegate type already exists EventHandler<T>, then directly declare a eventvariable. Finally, we raise the event when a match is found in the file.

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));
        }
    }
}

Defining and raising similar field events

The easiest way to add an event class is declared as the public fields of the event, as shown in the section:

public event EventHandler<FileFoundArgs> FileFound;

This seems to be a statement in the public domain, is not a good object-oriented design, but the compiler does generate a wrapper, and only in a secure way to access the event object. The event is similar to field the only available action is to add / delete handler:

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

Here, although there are onFileFoundlocal variables, but because it is defined by the lambda, the deletion will not work properly.

It is worth mentioning that the code outside the class can not raise events and other events conducted operations.

The return value from the event subscribers

We consider a new feature: canceled.

Here the scene is set trigger FileFoundevent, check if the file is the last to meet the requirements of the file, then the event source component should stop next action.

Since the event handler (event subscriber) does not return a value, so you need to transfer information in other ways. Standard mode uses the event EventArgsobject that contains some fields (event subscriber can change these fields to pass information).

Based on the "Cancel" semantics can be used in two different modes (in either mode requires EventArgsone of boolfield).

Mode 1 allows any event subscriber to cancel the operation. In this mode boolfield is initialized false, any event subscriber can change it to true, when all subscribers receive event notification event occurs, FileSearcherwill check this value and take further action.

Model II when all event subscriber wants to cancel further action, FileSearcheronly to cancel next action. In this mode the boolfield is initialized true, any event subscriber can change it to false(indicate the next action may continue). When all subscribers receive event notification event occurs, FileSearcherwe will check this value and take further action. This model has an extra step: initiating event components need to know if there are any subscribers receive the event. If there are no subscribers, the field will falsely indicate.

Let's look at an example of a pattern, you first need to EventArgsadd in a boolfield CancelRequested:

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

This field is automatically initialized false, the component needs to check the flag after that caused the event to determine whether there is any subscriber request to cancel next action:

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;
    }
}

The advantage of this model is that it does not cause significant changes that increase after a new inspection items, subscribers are not required to cancel before, now is not about this new examination items canceled. Unless the user wants to check for new subscribers to support the field. Such a very loose coupling.

We update a subscriber, after finding it in the first executable source component to cancel the event next action.

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

Another example

Let's look at one more example that demonstrates another conventional method events. The example we traverse all subdirectories. In the directory with many subdirectories, which can be a lengthy operation, let's add an event, the event is raised whenever a new directory search begins. This allows subscribers can track progress, report progress to the user. This time we see this event as an internal event. This means EventArgscan also be set privatein.

First create a new EventArgsderived class for the new report catalog and progress.

//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;
    }
}

Define an event, try this different syntax, in addition to using field syntax, you can also use the add and remove handlers explicitly created property. These handlers do not need extra code, but it shows how to create them.

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

That code above is compiler defined for the field events you saw earlier implicitly generated code, under normal circumstances, it is to create an event using the previous attribute syntax is very similar.

Let's look together Searchmethod, which loops through all subdirectories, and triggered two events.

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;
    }
}  

If directoryChangedthere are no subscribers, use ?.Invoke()phrases can ensure it is working properly.

Here are the logical subscribers when the event is raised, Print and complete directory to check the progress of the console.

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

The event is C#an important mode, by learning it, you can quickly write the usual C#and .NETcode. These modes will next be seen in the latest version of .NETchanges some.

Update .Net Core Event Mode

.NET CoreMore relaxed mode, EventHandler<TEventArgs>the definition are no longer TEventArgsto be from System.EventArgsthe constraints derived class.

In order to increase the flexibility and backward compatible, System.EventArgsclass introduces a method MemberwoseClone(), a shallow clone of the created object, the method must be reflective, so that from EventArgsany class that implements the function derived.

You can also SearchDirectoryArgschange the structure

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;
    }
}

You should not be FileFoundArgschanged from the value type reference type, or event source component can not be observed modify any subscribers.

Let's look at how this modification backward compatible, drop constraints will not affect any existing code, any existing event parameter types from any course can be System.EventArgsderived. Backward compatibility is that they can continue from System.EventArgsone of the main reasons derived. So now created a new type will not have any subscribers in the existing code base.

Asynchronous subscribers of events

The last mode you need to learn: how to write event subscriber call asynchronous code correctly. This will be after the async and awaitintroduction article.

Distinguish between agents and events

In the commission to decide between design and event-based design, based for .Net Corethe novice platform is often usually strenuous. Because the function of the two languages are very similar. Event is even by the commission to build the language. They have in common the following:

  • Provides late-binding mechanism (components communicate only by calling the method when run know).
  • Supports single and multiple subscribers.
  • Have similar syntax to add and remove handlers.
  • Raise events and delegating the call using the same syntax.
  • Support Invoke()and .?used together.

Listen to the event is optional

When determining which language to use features, the most important factor to consider is whether there must be attached subscribers . If your code must subscribe to the code's call, you should use the delegate-based design, code if you can complete all its work without calling any subscriber, you should use the event-based design.

Combined with the previous example a consideration. Use List.Sort()must be correct in order to provide a comparison function sorts the elements. LINQQueries must be provided with the commission in order to determine the elements returned. Both should be designed based on the principal use. (This is equivalent to delegate as parameter method has been tried, event does not serve as parameter method)

Examples of binding Benpian above considerations. ProgressEvents for the report task progress, regardless of whether any subscribers, the task will continue. FileSearcherEvent is another example, even if no additional events subscribers, it will still search for and find all the files you are looking for. Both should use event-based design.

### return to the values ​​need to commission

Delegate type for the event has an invalid return type, although we can use EventArgsto pass parameters, but it is better to return results directly from the method so natural.

So when the event subscriber has a return value, we chose based delegate.

Event subscribers usually have a longer life cycle

This is a weaker justification, but you may find that, when the event source component will raise the event in a very long period of time, event-based design will be more natural. You can see an example for UX controls on many systems, event subscription, event source could trigger event throughout the life cycle of the program.

This is based on the design parameters of many delegate contrast, in the design based on the principal, the method is used as the delegate, and the method returns no longer used after the delegation.

Careful selection

The above considerations are not mandatory, contrary as they can help guide your choice. They are very good to deal with late binding scheme. Select the information that best convey your design.

Guess you like

Origin www.cnblogs.com/czjk/p/12112439.html