Skip to content

Commit 3618803

Browse files
authored
use code includes over inline copies (#5600)
* use code includes over inline copies Fixes #887 Relies on dotnet/samples#80 Provide links to downloadable samples, and update incline samples to use include snippets from the sample. * fix build errors A couple tags were mis-typed * respond to feedback Reviews from @JRAlexander and @repetrusha * fix one last typo
1 parent 3f477b5 commit 3618803

File tree

5 files changed

+34
-239
lines changed

5 files changed

+34
-239
lines changed

docs/csharp/delegate-class.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,8 @@ Suppose you wanted to sort a list of strings by their length. Your
109109
comparison function might be the following:
110110

111111
```csharp
112-
private static int CompareLength(string left, string right)
113-
{
114-
return left.Length.CompareTo(right.Length);
115-
}
112+
private static int CompareLength(string left, string right) =>
113+
left.Length.CompareTo(right.Length);
116114
```
117115

118116
The method is declared as a private method. That's fine. You may not

docs/csharp/delegates-events.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ This topic will be covered under the following articles:
4141

4242
This article discusses how you should distinguish between using events and delegates in your designs.
4343

44+
You can download the [delegates sample](https://github.com/dotnet/samples/tree/master/csharp/delegates-and-events) and the [events sample](https://github.com/dotnet/samples/tree/master/csharp/events) from our GitHub samples repository.

docs/csharp/delegates-patterns.md

Lines changed: 10 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -78,35 +78,18 @@ Let's start small: the initial implementation will accept new messages,
7878
and write them using any attached delegate. You can start with one delegate
7979
that writes messages to the console.
8080

81-
```csharp
82-
public static class Logger
83-
{
84-
public static Action<string> WriteMessage;
85-
86-
public static void LogMessage(string msg)
87-
{
88-
WriteMessage(msg);
89-
}
90-
}
91-
```
81+
[!code-csharp[LoggerImplementation](../../samples/csharp/delegates-and-events/Logger.cs#FirstImplementation "A first Logger implementation.")]
9282

9383
The static class above is the simplest thing that can work. We need to
9484
write the single implementation for the method that writes messages
9585
to the console:
9686

97-
```csharp
98-
public static void LogToConsole(string message)
99-
{
100-
Console.Error.WriteLine(message);
101-
}
102-
```
87+
[!code-csharp[LogToConsole](../../samples/csharp/delegates-and-events/Program.cs#LogToConsole "A Console logger.")]
10388

10489
Finally, you need to hook up the delegate by attaching it to
10590
the WriteMessage delegate declared in the logger:
10691

107-
```csharp
108-
Logger.WriteMessage += LogToConsole;
109-
```
92+
[!code-csharp[ConnectDelegate](../../samples/csharp/delegates-and-events/Program.cs#ConnectDelegate "Connect to the delegate")]
11093

11194
## Practices
11295

@@ -130,50 +113,14 @@ creating other logging mechanisms.
130113
Next, let's add a few arguments to the `LogMessage()` method so that
131114
your log class creates more structured messages:
132115

133-
```csharp
134-
// Logger implementation two
135-
public enum Severity
136-
{
137-
Verbose,
138-
Trace,
139-
Information,
140-
Warning,
141-
Error,
142-
Critical
143-
}
144-
145-
public static class Logger
146-
{
147-
public static Action<string> WriteMessage;
148-
149-
public static void LogMessage(Severity s, string component, string msg)
150-
{
151-
var outputMsg = $"{DateTime.Now}\t{s}\t{component}\t{msg}";
152-
WriteMessage(outputMsg);
153-
}
154-
}
155-
```
116+
[!code-csharp[Severity](../../samples/csharp/delegates-and-events/Logger.cs#Severity "Define severities")]
117+
[!code-csharp[NextLogger](../../samples/csharp/delegates-and-events/Logger.cs#LoggerTwo "Refine the Logger")]
156118

157119
Next, let's make use of that `Severity` argument to filter the messages
158120
that are sent to the log's output.
159121

160-
```csharp
161-
public static class Logger
162-
{
163-
public static Action<string> WriteMessage;
164-
165-
public static Severity LogLevel {get;set;} = Severity.Warning;
166-
167-
public static void LogMessage(Severity s, string component, string msg)
168-
{
169-
if (s < LogLevel)
170-
return;
171-
172-
var outputMsg = $"{DateTime.Now}\t{s}\t{component}\t{msg}";
173-
WriteMessage(outputMsg);
174-
}
175-
}
176-
```
122+
[!code-csharp[FinalLogger](../../samples/csharp/delegates-and-events/Logger.cs#LoggerFinal "Finish the Logger")]
123+
177124
## Practices
178125

179126
You've added new features to the logging infrastructure. Because
@@ -198,42 +145,13 @@ each message is generated.
198145

199146
Here is that file based logger:
200147

201-
```csharp
202-
public class FileLogger
203-
{
204-
private readonly string logPath;
205-
public FileLogger(string path)
206-
{
207-
logPath = path;
208-
Logger.WriteMessage += LogMessage;
209-
}
210-
211-
public void DetachLog() => Logger.WriteMessage -= LogMessage;
212-
213-
// make sure this can't throw.
214-
private void LogMessage(string msg)
215-
{
216-
try {
217-
using (var log = File.AppendText(logPath))
218-
{
219-
log.WriteLine(msg);
220-
log.Flush();
221-
}
222-
} catch (Exception e)
223-
{
224-
// Hmm. Not sure what to do.
225-
// Logging is failing...
226-
}
227-
}
228-
}
229-
```
148+
[!code-csharp[FileLogger](../../samples/csharp/delegates-and-events/FileLogger.cs#FileLogger "Log to files")]
149+
230150

231151
Once you've created this class, you can instantiate it and it attaches
232152
its LogMessage method to the Logger component:
233153

234-
```csharp
235-
var file = new FileLogger("log.txt");
236-
```
154+
[!code-csharp[FileLogger](../../samples/csharp/delegates-and-events/Program.cs#FileLogger "Log to files")]
237155

238156
These two are not mutually exclusive. You could attach both log
239157
methods and generate messages to the console and a file:

docs/csharp/event-pattern.md

Lines changed: 18 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,7 @@ robust algorithms.
5858
Here is the initial event argument declaration for finding a sought
5959
file:
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

7363
Even though this type looks like a small, data-only type, you should
7464
follow the convention and make it a reference (`class`) type. That
@@ -87,48 +77,27 @@ specialization.
8777
Let's fill out the FileSearcher class to search for files that match
8878
a 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

10784
The 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

11489
This 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
11691
through properties, or methods. While this make look like a bad
11792
practice, the code generated by the compiler does create wrappers so
11893
that the event objects can only be accessed in safe ways. The only
11994
operations 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

12798
and 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

133102
Note that there's a local variable for the handler. If you used
134103
the 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
171140
indicate a cancel incorrectly.
172141

173142
Let'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
192149
check the flag after raising the event to see if any of the
193150
subscribers have requested a cancellation:
194151

@@ -206,7 +163,7 @@ public void List(string directory, string searchPattern)
206163
```
207164

208165
One 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
210167
not. None of the subscriber code needs updating unless they want to
211168
support 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.
237194
You'll start by creating the new EventArgs derived class for
238195
reporting 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

256199
Again, you can follow the recommendations to make an immutable
257200
reference type for the event arguments.
258201

259202
Next, define the event. This time, you'll use a different syntax. In
260203
addition to using the field syntax, you can explicitly create the
261204
property, 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
263206
you 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
275211
generates for the field event definitions you've seen earlier. You
276212
create 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
285221
this is to use a default argument to specify that you want to search
286222
all 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

326226
At this point, you can run the application calling the overload for
327227
searching 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

342236
You've seen patterns that are followed throughout the .NET ecosystem.
343237
By learning these patterns and conventions, you'll be writing

0 commit comments

Comments
 (0)