@@ -58,17 +58,7 @@ robust algorithms.
5858Here is the initial event argument declaration for finding a sought
5959file:
6060
61- ``` csharp
62- public class FileFoundArgs : EventArgs
63- {
64- public string FoundFile { get ; }
65-
66- public FileFoundArgs (string fileName )
67- {
68- FoundFile = fileName ;
69- }
70- }
71- ```
61+ [ !code-csharp[ EventArgs] ( ../../samples/csharp/events/Program.cs#EventArgsV1 " Define event arguments ")]
7262
7363Even though this type looks like a small, data-only type, you should
7464follow the convention and make it a reference (` class ` ) type. That
@@ -87,48 +77,27 @@ specialization.
8777Let's fill out the FileSearcher class to search for files that match
8878a pattern, and raise the correct event when a match is discovered.
8979
90- ``` csharp
91- public class FileSearcher
92- {
93- public event EventHandler <FileFoundArgs > FileFound ;
94-
95- public void Search (string directory , string searchPattern )
96- {
97- foreach (var file in Directory .EnumerateFiles (directory , searchPattern ))
98- {
99- FileFound ? .Invoke (this , new FileFoundArgs (file ));
100- }
101- }
102- }
103- ```
80+ [ !code-csharp[ FileSearxcher] ( ../../samples/csharp/events/Program.cs#FileSearcherV1 " Create the initial file searcher ")]
10481
10582## Definining and Raising Field-Like Events
10683
10784The simplest way to add an event to your class is to declare that
108- event as a public field, as in the above example:
85+ event as a public field, as in the preceding example:
10986
110- ``` csharp
111- public event EventHandler < FileFoundArgs > FileFound ;
112- ```
87+ [ !code-csharp[ DeclareEvent] ( ../../samples/csharp/events/Program.cs#DeclareEvent " Declare the file found event ")]
11388
11489This looks like it's declaring a public field, which would appear to
115- be bad object oriented practice. You want to protect data access
90+ be bad object- oriented practice. You want to protect data access
11691through properties, or methods. While this make look like a bad
11792practice, the code generated by the compiler does create wrappers so
11893that the event objects can only be accessed in safe ways. The only
11994operations available on a field-like event are add handler:
12095
121- ``` csharp
122- EventHandler < FileFoundArgs > onFileFound = (sender , eventArgs ) =>
123- Console .WriteLine (eventArgs .FoundFile );
124- lister .FileFound += onFileFound ;
125- ```
96+ [ !code-csharp[ DeclareEventHandler] ( ../../samples/csharp/events/Program.cs#DeclareEventHandler " Declare the file found event handler ")]
12697
12798and remove handler:
12899
129- ``` csharp
130- lister .FileFound -= onFileFound ;
131- ```
100+ [ !code-csharp[ RemoveEventHandler] ( ../../samples/csharp/events/Program.cs#RemoveHandler " Remove the event handler ")]
132101
133102Note that there's a local variable for the handler. If you used
134103the body of the lambda, the remove would not work correctly. It would
@@ -171,24 +140,12 @@ have seen the event. If there are no subscribers, the field would
171140indicate a cancel incorrectly.
172141
173142Let's implement the first version for this sample. You need to add a
174- boolean field to the FileFoundEventArgs type:
143+ boolean field named ` CancelRequested ` to the ` FileFoundArgs ` type:
175144
176- ``` csharp
177- public class FileFoundArgs : EventArgs
178- {
179- public string FoundFile { get ; }
180- public bool CancelRequested { get ; set ; }
145+ [ !code-csharp[ EventArgs] ( ../../samples/csharp/events/Program.cs#EventArgs " Update event arguments ")]
181146
182- public FileFoundArgs (string fileName )
183- {
184- FoundFile = fileName ;
185- }
186- }
187- ```
188-
189- This new Field should be initialized to false, so you don't cancel
190- for no reason. That is the default value for a boolean field, so that
191- happens automatically. The only other change to the component is to
147+ This new Field is automatically initialized to ` false ` , the default value for a Boolean field, so you don't cancel
148+ accidentally. The only other change to the component is to
192149check the flag after raising the event to see if any of the
193150subscribers have requested a cancellation:
194151
@@ -206,7 +163,7 @@ public void List(string directory, string searchPattern)
206163```
207164
208165One advantage of this pattern is that it isn't a breaking change.
209- None of the subscribers requested a cancel before, and they still are
166+ None of the subscribers requested a cancellation before, and they still are
210167not. None of the subscriber code needs updating unless they want to
211168support the new cancel protocol. It's very loosely coupled.
212169
@@ -237,41 +194,20 @@ can also make the types used for the arguments internal as well.
237194You'll start by creating the new EventArgs derived class for
238195reporting the new directory and progress.
239196
240- ``` csharp
241- internal class SearchDirectoryArgs : EventArgs
242- {
243- internal string CurrentSearchDirectory { get ; }
244- internal int TotalDirs { get ; }
245- internal int CompletedDirs { get ; }
246-
247- internal SearchDirectoryArgs (string dir , int totalDirs , int completedDirs )
248- {
249- CurrentSearchDirectory = dir ;
250- TotalDirs = totalDirs ;
251- CompletedDirs = completedDirs ;
252- }
253- }
254- ```
197+ [ !code-csharp[ DirEventArgs] ( ../../samples/csharp/events/Program.cs#SearchDirEventArgs " Define search directory event arguments ")]
255198
256199Again, you can follow the recommendations to make an immutable
257200reference type for the event arguments.
258201
259202Next, define the event. This time, you'll use a different syntax. In
260203addition to using the field syntax, you can explicitly create the
261204property, with add and remove handlers. In this sample, you won't
262- need extra code in those handlers in this project , but this shows how
205+ need extra code in those handlers, but this shows how
263206you would create them.
264207
265- ``` csharp
266- internal event EventHandler < SearchDirectoryArgs > DirectoryChanged
267- {
268- add { directoryChanged += value ; }
269- remove { directoryChanged -= value ; }
270- }
271- private EventHandler < SearchDirectoryArgs > directoryChanged ;
272- ```
208+ [ !code-csharp[ Declare event with add and remove handlers] ( ../../samples/csharp/events/Program.cs#DeclareSearchEvent " Declare the event with add and remove handlers ")]
273209
274- In may ways, the code you write here mirrors the code the compiler
210+ In many ways, the code you write here mirrors the code the compiler
275211generates for the field event definitions you've seen earlier. You
276212create the event using syntax very similar to that used for
277213[ properties] ( properties.md ) . Notice that the handlers have different
@@ -285,43 +221,7 @@ subdirectories and raises both events. The easiest way to accomplish
285221this is to use a default argument to specify that you want to search
286222all directories:
287223
288- ``` csharp
289- public void Search (string directory , string searchPattern , bool searchSubDirs = false )
290- {
291- if (searchSubDirs )
292- {
293- var allDirectories = Directory .GetDirectories (directory , " *.*" , SearchOption .AllDirectories );
294- var completedDirs = 0 ;
295- var totalDirs = allDirectories .Length + 1 ;
296- foreach (var dir in allDirectories )
297- {
298- directoryChanged ? .Invoke (this ,
299- new SearchDirectoryArgs (dir , totalDirs , completedDirs ++ ));
300- // Recursively search this child directory:
301- SearchDirectory (dir , searchPattern );
302- }
303- // Include the Current Directory:
304- directoryChanged ? .Invoke (this ,
305- new SearchDirectoryArgs (directory , totalDirs , completedDirs ++ ));
306- SearchDirectory (directory , searchPattern );
307- }
308- else
309- {
310- SearchDirectory (directory , searchPattern );
311- }
312- }
313-
314- private void SearchDirectory (string directory , string searchPattern )
315- {
316- foreach (var file in Directory .EnumerateFiles (directory , searchPattern ))
317- {
318- var args = new FileFoundArgs (file );
319- FileFound ? .Invoke (this , args );
320- if (args .CancelRequested )
321- break ;
322- }
323- }
324- ```
224+ [ !code-csharp[ SearchImplementation] ( ../../samples/csharp/events/Program.cs#FinalImplementation " Implementation to search directories ")]
325225
326226At this point, you can run the application calling the overload for
327227searching all sub-directories. There are no subscribers on the new
@@ -331,13 +231,7 @@ that this works correctly.
331231 Let's add a handler to write a line that shows the progress in the
332232 console window.
333233
334- ``` csharp
335- lister .DirectoryChanged += (sender , eventArgs ) =>
336- {
337- Console .Write ($" Entering '{eventArgs .CurrentSearchDirectory }'." );
338- Console .WriteLine ($" {eventArgs .CompletedDirs } of {eventArgs .TotalDirs } completed..." );
339- };
340- ```
234+ [ !code-csharp[ Search] ( ../../samples/csharp/events/Program.cs#Search " Declare event handler ")]
341235
342236You've seen patterns that are followed throughout the .NET ecosystem.
343237By learning these patterns and conventions, you'll be writing
0 commit comments