|
|
|
Title:
|
BetterKnowAFramework
|
Language:
|
C#
|
|
Description:
|
BetterKnowAFramework
|
Views:
|
508
|
|
Author:
|
Peter Parker
|
Date Added:
|
4/28/2008
|
Copy
|
Code
|
|
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Diagnostics;
5using System.Globalization;
6using System.IO;
7using System.IO.Compression;
8using System.Linq;
9using System.Linq.Expressions;
10using System.Reflection;
11using System.Security;
12using System.Security.Cryptography;
13using System.Security.Permissions;
14using System.Security.Policy;
15using System.Text;
16using System.Text.RegularExpressions;
17using System.Threading;
18using System.Xml;
19
20namespace Moserware.BetterKnowAFramework
21{
22 static class Program
23 {
24 static void Main(string[] args)
25 {
26 #region About me ...
27
28 // Jeff Moser
29 // jeff@moserware.com
30 // My Blog: http://www.moserware.com/
31 #endregion
32
33 // I gave this talk at Indy Code Camp on Saturday, April 26, 2008
34 // The format was just stepping through this file by hitting F11
35 // in the debugger and discussing things as they came up.
36 #region ...
37
38 // Poll
39 // How many of you have used:
40 // 1. System.Text.StringBuilder
41 // 2. System.IO.Path
42 // 3. System.Text.RegularExpressions.Regex
43 // 4. System.IFormattable
44 // 5. System.AppDomain
45 // 6. System.Linq.Expressions.Expression
46
47 // Poll results: mostly 1 & 2, some 3, couple of 5.
48 #endregion
49 #region Startup Things (a.k.a. "How did we get here?") ...
50
51 // Your OS starts a process:
52 // System.Diagnostics.Process.Start()
53
54 // Then a thread gets queued up:
55 // System.Threading.Thread.Start()
56
57 // Now, the CLR will execute your assembly:
58 // System.AppDomain.ExecuteAssembly()
59 // System.AppDomain._nExecuteAssembly (internal call)
60
61 // It does this by loading your code and finding the
62 // static method marked with ".entrypoint" in IL
63
64 System.Diagnostics.Process myProcess = Process.GetCurrentProcess();
65 AppDomain myAppDomain = AppDomain.CurrentDomain;
66
67 // I'll be using C# 3's type-inference from now on by
68 // using "var". It's just as if I typed everything out:
69 var myAssembly = Assembly.GetEntryAssembly();
70 var myThread = Thread.CurrentThread;
71
72 var greeting = "Hello World!";
73 #endregion
74 #region Beginner ...
75
76 WarmupClasses();
77 ExploringSystemCollectionsGeneric();
78 FunWithStrings();
79 BoolVsEnumDebate();
80 #endregion
81 #region Intermediate ...
82
83 ILoveRegularExpressions();
84 OverviewOfStreams();
85 DabblingWithSecurity();
86 HowDoWeRelate();
87 PeeringIntoIDisposable();
88 AggregatesMakeMortHappy();
89 // GAC -- look in the virtual C:\windows\assembly folder
90 #endregion
91 #region A bit more challenging ...
92
93
94 UnderstandingHowLinqIsImplemented();
95 DoesYourCodePassTheTurkeyTest();
96 SewingWithThreads();
97 EventsCanBeTricky();
98 BriefCoverageOfTracing();
99 InterestingInternalClasses();
100
101 // Juval's C# Coding Conventions
102 // http://www.idesign.net/idesign/download/IDesign%20CSharp%20Coding%20Standard.zip
103
104 // Recommend reading "Framework Design Guidelines" by Cwalina and Abrams
105 // digest at: http://blogs.msdn.com/kcwalina/archive/2008/04/09/FDGDigest.aspx
106 // (new updated edition coming out later this year)
107 // naming: callback, canceled, Email, FileName, HashTable, Id id, Indexes, Ok, ok, Pi, signIn, userName, whitespace, writable vs writeable
108
109 // Expert .NET 2.0 IL Assembler by Serge Lidin
110
111 // Obscure classes, but helpful
112 // System.WeakReference
113 // System.ComponentModel.ISynchronizeInvoke
114 #endregion
115
116 // Questions?
117 }
118
119 private static void WarmupClasses()
120 {
121 #region System.IO.Path ...
122
123 // Very useful class I didn't know about for awhile
124 // Helps manipulate file paths
125
126 var sampleFileName = @"C:\Windows\System32\calc.exe";
127 var sampleDirectory = Path.GetDirectoryName(sampleFileName);
128 var isPathRootedYes = Path.IsPathRooted(sampleFileName);
129 var isPathRootedNo = Path.IsPathRooted("helloworld.exe");
130 var calcexe = Path.GetFileName(sampleFileName);
131 var otherFileInDirectory = Path.Combine(sampleDirectory, "cmd.exe");
132 #endregion
133 #region System.Math (Show #293) ...
134
135 // Convenient math functions
136
137 var max = Math.Max(4, 2);
138 var min = Math.Min(4, 2);
139 var abs = Math.Abs(-42);
140 var tan = Math.Tan(Math.PI / 4);
141 var floor = Math.Floor(4.2);
142 var sin = Math.Sin(Math.PI / 4);
143
144 // This one returns the full 64 bit result
145 var bigMul = Math.BigMul(int.MaxValue, int.MaxValue);
146 #endregion
147 #region System.Console (Show #268) ...
148
149 var capsLockOn = Console.CapsLock;
150 var numberLockOn = Console.NumberLock;
151 Console.Title = "Better Know a Framework";
152 Console.BackgroundColor = ConsoleColor.Blue;
153 Console.ForegroundColor = ConsoleColor.Yellow;
154 Console.Clear();
155 Console.WriteLine("Hello World!");
156
157 Console.BackgroundColor = ConsoleColor.Black;
158 Console.ForegroundColor = ConsoleColor.Gray;
159 Console.Clear();
160 #endregion
161 #region Convert (Show #280) ...
162
163 var trueIs1point0 = Convert.ToDouble(true);
164 var goingBeyondInt32 = Convert.ToUInt32(int.MaxValue) + 1;
165
166 var base64Example = Convert.ToBase64String(BitConverter.GetBytes(0xDEADBEEF));
167 var returnTrip = Convert.FromBase64String(base64Example);
168 #endregion
169 #region System.Xml.XmlConvert ...
170
171 var xmlDate = XmlConvert.ToString(DateTime.Now);
172 var traditionalDate = DateTime.Now.ToString();
173 var xmlTrue = XmlConvert.ToBoolean("1")
174 &&
175 XmlConvert.ToBoolean("true");
176 #endregion
177 #region System.Environment (Show #269) ...
178
179 var userName = Environment.UserName;
180
181 // The following method is very useful because these "special folders" can
182 // be in many different spots depending on the OS and language
183 var programFilesFolder = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
184 #endregion
185 #region System.TimeSpan (Show #294) ...
186
187
188 DateTime thirtyMinutesFromNow = DateTime.Now + TimeSpan.FromMinutes(30);
189 #endregion
190
191 }
192
193 private static void FunWithStrings()
194 {
195 #region System.Text.StringBuilder (Show #249) ...
196
197
198 // If you are concatenating several strings together, this is bad:
199 var hello = "Hello";
200 var space = " ";
201 var world = "World!";
202 var badHelloWorld = hello + space + world; // (hello + space) + world
203
204 // The reason is that everytime you concat two things, a temporary
205 // intermediate object is created.
206
207 // Here's the preferred way:
208
209 var sb = new System.Text.StringBuilder();
210 sb.Append("Hello");
211 sb.Append(" ");
212 sb.Append("World!");
213 var helloWorld = sb.ToString();
214
215 // Someone in the audience asked when you should start caring about
216 // the inefficiencies. I said if you know you're going to be concatenating
217 // more than 5 strings than prefer StringBuilder
218 #endregion
219 #region String formatting and IFormattable (Show #315) ...
220
221
222 // note that MyNumber is my custom class that implments IFormattable
223 var currentYear = new MyNumber(DateTime.Now.Year);
224
225 // Here we're showing off the custom formatting.
226 // Note that "roman" is my own format.
227
228 string formattedGreeting
229 = string.Format(@"Hello! My name is {0} and today is {1:D}." +
230 "The year is {2:0,0} ({2:roman})",
231 "Jeff", DateTime.Now, currentYear);
232
233 // Here is the type of thing string.Format is calling under the covers
234 string year1999 = (new MyNumber(1999)).ToString("roman", CultureInfo.CurrentCulture);
235
236 string directoryService = string.Format("{0:(###) ###-####}", 8005551212);
237
238 // More string formatting examples at
239 // http://blog.stevex.net/index.php/string-formatting-in-csharp/
240 #endregion
241 }
242
243 private static void BoolVsEnumDebate()
244 {
245 // Which is easier to understand?
246
247 var exhibitA = string.Compare("Jeff", "jeff", true) == 0;
248 var exhibitB = string.Compare("Jeff", "jeff", StringComparison.CurrentCultureIgnoreCase) == 0;
249
250 // If you're designing public APIs, tend to prefer enums (e.g. StringComparison) over booleans.
251 // It just makes things easier to read.
252
253 }
254
255 private static void ILoveRegularExpressions()
256 {
257 // Here we're only going to show a few numbers. But just imagine
258 // If you had to process thousands or even millions.
259 string phoneNumbersOldFormat = "(800) 555-1212, (317) 222-1234, 765-4951234";
260 var newFormat = Regex.Replace(phoneNumbersOldFormat, @"[^\d,]*
261 (?<areaCode>\d{3})
262 \D*
263 (?<first3>\d{3})
264 \D*
265 (?<last4>\d{4})
266 [^,]*",
267 "${areaCode}.${first3}.${last4} ", RegexOptions.IgnorePatternWhitespace);
268
269 var isSsn = Regex.IsMatch("123-45-6789", @"\d{3}-\d{2}-\d{4}");
270 // many more possibilities
271 // see http://msdn2.microsoft.com/en-us/library/hs600312.aspx
272 }
273
274 private static void OverviewOfStreams()
275 {
276 // Streams are a generic concept in .net
277 // They're very swappable and connectable sort of like Legos.
278
279 // Let's create a stream that writes text, then compresses it,
280 // then encrypts it, then stores it in memory.
281
282
283 var memory = new MemoryStream();
284 var secretKey = "My Secret Password!";
285 var salt = new byte[32];
286 var iv = new byte[16];
287 var passwordBytes = new Rfc2898DeriveBytes(secretKey, salt);
288 var encryptionAlgorithm = Aes.Create().CreateEncryptor(passwordBytes.GetBytes(32), iv);
289
290 var encryption = new CryptoStream(memory, encryptionAlgorithm, CryptoStreamMode.Write);
291
292 var compression = new GZipStream(encryption, CompressionMode.Compress);
293
294 var writer = new StreamWriter(compression);
295
296 // The pipeline/stream is
297 // writer => compression => encryption => memory
298 // Question: Why would this be a bad idea?
299 // writer => encryption => compression => memory
300
301 // As I mentioned in the talk and people in the audience got,
302 // you wouldn't want to do the latter because encrypted data
303 // should be random and won't compress much.
304
305 // Quick way to generate "Hello" repeated 500 times.
306 var input = string.Join(" ", Enumerable.Repeat("Hello", 500).ToArray());
307 var inputSize = input.Length;
308 writer.Write(input);
309 writer.Flush();
310 var outputBytes = memory.ToArray();
311
312 // Write the file from the MemoryStream to disk:
313
314 var temporaryFileName = Path.GetTempFileName();
315
316 using (var outputStream = File.OpenWrite(temporaryFileName))
317 {
318 memory.WriteTo(outputStream);
319 }
320
321 memory.Close();
322
323 // No time to cover IsolateStorageStream, but it's useful.
324
325 }
326
327 private static void AggregatesMakeMortHappy()
328 {
329 // Aggregate Pattern
330 // See "Framework Design Guidelines" pages 240-243
331
332 // Mort is someone who wants to get things done.
333 // See: http://www.codinghorror.com/blog/archives/001004.html
334
335 // Mort likes 1 - 2 lines of code to get things done:
336 // var fileContents = File.ReadAllBytes(path)
337 // var wc = new System.Net.WebClient();
338 // var googlePage = wc.DownloadString("http://www.google.com/");
339 }
340
341 private static void DabblingWithSecurity()
342 {
343 #region System.Security.Cryptography.RandomNumberGenerator (Show #244) ...
344
345 // Question: Why not use System.Random for generating keys?
346 // Answer: It can be a source of easy attack:
347 // http://en.wikipedia.org/wiki/Random_number_generator_attack
348
349 var rng = RandomNumberGenerator.Create();
350 var myRandomBytes = new byte[10];
351 rng.GetBytes(myRandomBytes);
352 rng.GetBytes(myRandomBytes);
353 #endregion
354 #region System.Security.Cryptography.ProtectedData ...
355
356
357 // Here's a quick way of storing secrets in a way that only the current
358 // user can decrypt:
359 var pd = ProtectedData.Protect(ASCIIEncoding.ASCII.GetBytes("Hello World"), null, DataProtectionScope.CurrentUser);
360 var pdString = Convert.ToBase64String(pd);
361 var unprotected = ASCIIEncoding.ASCII.GetString(ProtectedData.Unprotect(pd, null, DataProtectionScope.CurrentUser));
362 #endregion
363 }
364
365
366 private static void InterestingInternalClasses()
367 {
368 // Didn't have time to cover this, but check out:
369 // see http://www.moserware.com/2008/01/borrowing-ideas-from-3-interesting.html
370 // System.Linq.Strings
371 // System.Linq.Error
372 // Microsoft.Contract.Contracts
373 }
374
375 private static void HowDoWeRelate()
376 {
377 #region System.IComparable (Show #305) ...
378
379
380 // You can completely change how your class is sorted/compared.
381 // Note that "MyNumber" class implements IComparable and its
382 // behavior is to sort in descending order.
383 var list = new List<MyNumber>();
384 list.Add(new MyNumber(5));
385 list.Add(new MyNumber(1));
386 list.Add(new MyNumber(3));
387 list.Add(new MyNumber(2));
388 list.Sort();
389 #endregion
390
391 // I didn't cover this but:
392 // If just want equality, override object.Equals(obj)
393 // Question: Why might you want to implement IEquatable?
394 // Answer: It helps value types not box
395
396 // By the way, if you overload Equals, you should overload GetHashCode()
397 // or else things that depend on hash codes (like Dictionary) will
398 // cause confusing results.
399
400 }
401
402 private static void DoesYourCodePassTheTurkeyTest()
403 {
404 // I gave a brief overview of this post:
405 // http://www.moserware.com/2008/02/does-your-code-pass-turkey-test.html
406
407 Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");
408 DateTime julyFourthA = DateTime.Parse("07/04/2008");
409 DateTime julyFourthB = DateTime.Parse("07/04/2008", DateTimeFormatInfo.InvariantInfo);
410
411 double fourPointFiveA = double.Parse("4.5");
412 double fourPointFiveB = double.Parse("4.5", NumberFormatInfo.InvariantInfo);
413
414 string fileUpperA = "file".ToUpper();
415 string fileUpperB = "file".ToUpperInvariant();
416 bool wacky = string.Equals(fileUpperA, fileUpperB);
417
418 bool sampleCompare = "File".Equals(fileUpperB, StringComparison.OrdinalIgnoreCase);
419 }
420
421 private static void SewingWithThreads()
422 {
423 #region Threading ...
424
425 // Simple class to do things in the background
426 var bgw = new System.ComponentModel.BackgroundWorker();
427
428 // Threads can be expensive to create. So it's better to have a "pool"
429 // of threads that you can use and then have go to sleep:
430 ThreadPool.QueueUserWorkItem(
431 new WaitCallback(
432 delegate(object args)
433 {
434 for (int i = 0; i < 10; i++)
435 {
436 Thread.Sleep(1000);
437 }
438 }));
439
440
441 var domainNameToIP = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
442
443 object sync = new object();
444
445 // Imagine that you had a naive DNS server that locked every time
446 // it touched the dictionary (including reads)
447
448 lock (sync)
449 {
450 domainNameToIP["Moserware.com"] = "72.3.2.1";
451 }
452
453 // Question: Anything bad about this?
454 // Answer: YES! Very inefficient. Don't need to lock out
455 // people unless a writer occurs. It's better to use
456 // a ReaderWriterLock (or ReaderWriterLockSlim) instead.
457
458 var rwl = new ReaderWriterLock();
459 try
460 {
461 rwl.AcquireWriterLock(Timeout.Infinite);
462 domainNameToIP["moserware.com"] = "72.14.207.121";
463 }
464 finally
465 {
466 rwl.ReleaseWriterLock();
467 }
468
469
470 // no time to cover semaphore, autoresetevent, manualresetevent, waithandle, waitall
471
472 //int myNum = 1;
473 //Interlocked.Increment(ref myNum);
474
475 // Future:
476 // Parallel.For(0, 1000000, i=>
477 // {
478 // result[i] = ReallyExpensiveFunction(i);
479 // }
480
481 // automatically scales to multiple cores.
482 // See http://msdn2.microsoft.com/en-us/magazine/cc163340.aspx
483 #endregion
484 }
485
486 private static void EventsCanBeTricky()
487 {
488 var c = new SampleEventRaisingClass();
489 c.SomethingHappened += (s, e) => Console.WriteLine("Something Happened");
490 c.OnSomethingHappened();
491 }
492
493 private static void BriefCoverageOfTracing()
494 {
495 #region Shows #333 and #334 ...
496
497
498 // printf all grown up
499 // System.Diagnostics
500 Trace.Listeners.Add(new TextWriterTraceListener(Path.GetTempFileName()));
501 Trace.WriteLine("Hello from BriefCoverageOfTracing");
502
503 // Can be used for diagnosing bugs on customers machines if you have Trace logs.
504 #endregion
505
506 }
507
508 private static void UnderstandingHowLinqIsImplemented()
509 {
510 // A look under the covers of how LINQ is implemented:
511
512 var numbers1To50 = Enumerable.Range(1, 50);
513
514 // This LINQ statement in the SQL-esque syntax:
515 var evenNumbersSquared = from n in numbers1To50 where (n % 2 == 0) select n * n;
516
517 // is identical to this:
518 var alternately = numbers1To50.Where(n => n % 2 == 0).Select(n => n * n);
519
520 // but could also be rewritten this way (note that I define GetEvens and GetSquares below)
521 var orHowAbout = numbers1To50.GetEvens().GetSquares();
522
523 // You can do this manually too:
524 Func<int, bool> isEven = n => (n % 2) == 0;
525
526 // or using the uglier C# 2 syntax:
527 Func<int, int> squarer = delegate(int n)
528 {
529 return n * n;
530 };
531
532 var yetAnother = numbers1To50.Where(isEven).Select(squarer);
533
534 // More advanced uses of LINQ methods:
535
536 Func<int, int> factorial = n => Enumerable.Range(1, n).Aggregate(1, (a, i) => a * i);
537 var fact5 = factorial(5);
538
539 var sum1To10 = Enumerable.Range(1, 10).Sum();
540
541 // Express yourself..
542 // See: http://www.codeproject.com/KB/cs/explore_lamda_exp.aspx
543
544 Expression<Func<int, int>> square = x => x * x;
545 BinaryExpression squareplus2 = Expression.Add(square.Body,
546 Expression.Constant(2));
547 Expression<Func<int, int>> expr = Expression.Lambda<Func<int, int>>(squareplus2,
548 square.Parameters);
549
550 Func<int, int> compile = expr.Compile();
551 var result = compile(5);
552
553 // LINQ works with both Func's and Expression's
554 // Expressions are used when you want to understand more of the intent
555 // of what the programmer wants to do (typically multiple operations)
556
557 // Show #319 - IQueryable and IQueryProvider
558
559 // The best way to learn for me was to listen straight from Anders:
560 // http://langnetsymposium.com/talks.asp (click on Anders' talk)
561
562 }
563
564 private static IEnumerable<int> GetEvens(this IEnumerable<int> collection)
565 {
566 foreach (int n in collection)
567 {
568 if (n % 2 == 0)
569 {
570 yield return n;
571 }
572 }
573 }
574
575 private static IEnumerable<int> GetSquares(this IEnumerable<int> collection)
576 {
577 foreach (int n in collection)
578 {
579 yield return n * n;
580 }
581 }
582
583 private static void PeeringIntoIDisposable()
584 {
585 #region IDisposable (Show #287) ...
586
587
588 FileStream fs = null;
589
590 string path = @"C:\windows\system32\calc.exe";
591 var firstKB = new byte[1024];
592
593 // This type of code is so repetitive...
594 try
595 {
596 fs = new FileStream(path, FileMode.Open, FileAccess.Read);
597 fs.Read(firstKB, 0, firstKB.Length);
598 }
599 finally
600 {
601 fs.Close();
602 }
603
604 // ..that this syntax does the similar thing since FileStream
605 // implements IDisposable
606
607 using (fs = new FileStream(path, FileMode.Open, FileAccess.Read))
608 {
609 fs.Read(firstKB, 0, firstKB.Length);
610 // fs.Dispose() called automatically which calls fs.Close()
611 }
612
613 // when your object is disposed, you should call GC.SuppressFinalize
614
615 // FYI: foreach loops use dispose pattern on the enumerator
616 // See http://www.moserware.com/2008/02/for-loops-using-i-i-enumerators-or-none.html
617 #endregion
618 }
619
620 private static void ExploringSystemCollectionsGeneric()
621 {
622 // Arrays are used internally for collections.
623 // You can usually forget this, but sometimes it's good to know
624 #region System.Collections.Generic ...
625
626 #region System.Collections.Generic.List<T> ...
627
628
629 var list = new List<int>(Enumerable.Range(1, 3));
630 list.Add(4);
631
632 var capacity = list.Capacity;
633 // Question: How fast will the following statement run?
634 list.Add(5);
635
636 // Answer: Slower than you might think since a new array is created of size
637 // 8 and elements are copied from the old one to the new one.
638
639 capacity = list.Capacity;
640
641 // Now you see why this is bad:
642
643 for (int i = 0; i < 10; i++)
644 {
645 list.Insert(0, i);
646 }
647
648 // Every time you insert at position 0, you have to create a new array and
649 // copy the old contents.
650
651 list.AddRange(Enumerable.Range(6, 10));
652
653 // This command reduces the size of the array to just what you need:
654 list.TrimExcess();
655
656 capacity = list.Capacity;
657
658 list.ForEach(i => Console.WriteLine(i));
659
660 // Same thing using C# 2 syntax
661 list.ForEach(delegate(int i) { Console.WriteLine(i); });
662
663 var trueForAll = list.TrueForAll(i => i > 0);
664
665 list.Sort();
666 #endregion
667 #region System.Collections.Generic.Dictionary<T> ...
668
669
670 // This type of constructor isn't as well known:
671 var dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
672
673 // But it lets you have case insensitive key operations:
674 dictionary["one"] = 1;
675 dictionary["Two"] = 2;
676 dictionary["ThReE"] = 3;
677
678 // so you can do this:
679 var mySum = dictionary["one"] + dictionary["two"] + dictionary["three"];
680
681 // If you do the normal way:
682 dictionary = new Dictionary<string, int>();
683
684 dictionary["one"] = 1;
685 dictionary["Two"] = 2;
686 dictionary["ThReE"] = 3;
687
688 // You get errors:
689 try
690 {
691 mySum = dictionary["one"] + dictionary["two"] + dictionary["three"];
692 }
693 catch (KeyNotFoundException knfe)
694 {
695 knfe = knfe;
696 }
697 #endregion
698 #region Show 253 ...
699
700 #region System.Collections.Generic.Stack<T> ...
701
702
703 var stack = new Stack<int>(Enumerable.Range(1, 10));
704 var stackVal = stack.Peek();
705 stackVal = stack.Pop();
706 stack.Push(42);
707 stackVal = stack.Pop();
708 #endregion
709 #region System.Collections.Generic.Queue<T> ...
710
711 var queue = new Queue<int>(Enumerable.Range(1, 10));
712 var queueValue = queue.Dequeue();
713 queue.Enqueue(11);
714 #endregion
715 #endregion
716 #region System.Collections.Generic.HashSet<T> ...
717
718 var hs1To10 = new HashSet<int>(Enumerable.Range(1, 10));
719
720 // note the argument type
721 hs1To10.IntersectWith(Enumerable.Range(5, 11));
722
723 hs1To10 = new HashSet<int>(Enumerable.Range(1, 10));
724 hs1To10.UnionWith(Enumerable.Range(5, 11));
725
726 hs1To10 = new HashSet<int>(Enumerable.Range(1, 10));
727 hs1To10.SymmetricExceptWith(Enumerable.Range(5, 11));
728
729 // Note that LINQ to objects has set operations as well.
730 // Many things you do in code can be thought of as operations on sets.
731 #endregion
732
733 // Did not cover, but good to know
734 // ReadOnlyCollection -- good return type
735 #endregion
736 }
737 #region AppDomain Sandbox ...
738
739
740 // Didn't have time to cover this, but AppDomains provide interesting isolation.
741 // This is how SilverLight can run arbitrary code safely.
742 // From http://msdn2.microsoft.com/en-us/library/bb763046.aspx
743 private static void ShowAppDomainSandbox()
744 {
745 // Create the permission set to grant to other assemblies.
746 // In this case we are granting the permissions found in the LocalIntranet zone.
747 PermissionSet pset = GetNamedPermissionSet("LocalIntranet");
748 if (pset == null)
749 return;
750 // Optionally you can create your own permission set by explicitly adding permissions.
751 // PermissionSet pset = new PermissionSet(PermissionState.None);
752 // pset.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
753 // pset.AddPermission(new UIPermission(PermissionState.Unrestricted));
754 AppDomainSetup ads = new AppDomainSetup();
755 // Identify the folder to use for the sandbox.
756 ads.ApplicationBase = "C:\\Sandbox";
757 // Copy the application to be executed to the sandbox.
758 File.Copy("HelloWorld.exe", "C:\\sandbox\\HelloWorld.exe", true);
759
760 Evidence hostEvidence = new Evidence();
761 // Commenting out the following two statements has no effect on the sample.
762 // The grant set is determined by the grantSet parameter, not the evidence
763 // for the assembly. However, the evidence can be used for other reasons,
764 // for example, isolated storage.
765 hostEvidence.AddHost(new Zone(SecurityZone.Intranet));
766 hostEvidence.AddHost(new Url("C:\\Sandbox"));
767
768 // Create the sandboxed domain.
769 AppDomain sandbox = AppDomain.CreateDomain(
770 "Sandboxed Domain",
771 hostEvidence,
772 ads,
773 pset,
774 GetStrongName(Assembly.GetExecutingAssembly()));
775 sandbox.ExecuteAssemblyByName("HelloWorld");
776
777 }
778
779
780 /// <summary>
781 /// Get a strong name that matches the specified assembly.
782 /// </summary>
783 /// <exception cref="ArgumentNullException">
784 /// if <paramref name="assembly"/> is null
785 /// </exception>
786 /// <exception cref="InvalidOperationException">
787 /// if <paramref name="assembly"/> does not represent a strongly named assembly
788 /// </exception>
789 /// <param name="assembly">Assembly to create a StrongName for</param>
790 /// <returns>A StrongName for the given assembly</returns>
791 ///
792 public static StrongName GetStrongName(Assembly assembly)
793 {
794 if (assembly == null)
795 throw new ArgumentNullException("assembly");
796
797 AssemblyName assemblyName = assembly.GetName();
798 Debug.Assert(assemblyName != null, "Could not get assembly name");
799
800 // Get the public key blob.
801 byte[] publicKey = assemblyName.GetPublicKey();
802 if (publicKey == null || publicKey.Length == 0)
803 throw new InvalidOperationException("Assembly is not strongly named");
804
805 StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey);
806
807 // Return the strong name.
808 return new StrongName(keyBlob, assemblyName.Name, assemblyName.Version);
809 }
810 private static PermissionSet GetNamedPermissionSet(string name)
811 {
812 IEnumerator policyEnumerator = SecurityManager.PolicyHierarchy();
813
814 // Move through the policy levels to the machine policy level.
815 while (policyEnumerator.MoveNext())
816 {
817 PolicyLevel currentLevel = (PolicyLevel)policyEnumerator.Current;
818
819 if (currentLevel.Label == "Machine")
820 {
821 NamedPermissionSet copy = currentLevel.GetNamedPermissionSet(name);
822 return (PermissionSet)copy;
823 }
824 }
825 return null;
826 }
827 #endregion
828 }
829
830 internal class MyNumber : IFormattable, IComparable
831 {
832 private readonly int m_Number;
833
834 public MyNumber(int number)
835 {
836 m_Number = number;
837 }
838 #region IFormattable Members ...
839
840
841 public string ToString(string format, IFormatProvider formatProvider)
842 {
843 if (format.Equals("roman", StringComparison.OrdinalIgnoreCase))
844 {
845 return RomanNumeral.Encode(m_Number);
846 }
847 else
848 {
849 return m_Number.ToString(format, formatProvider);
850 }
851 }
852 #endregion
853 #region IComparable Members ...
854
855
856 public int CompareTo(object obj)
857 {
858 return -1 * m_Number.CompareTo((obj as MyNumber).m_Number);
859 }
860 #endregion
861 }
862
863 class RomanNumeral
864 {
865 static private Dictionary<char, int> m_LetterToValue = new Dictionary<char, int>();
866 static private char[] m_MostToLeast = new char[] { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
867
868 static RomanNumeral()
869 {
870 m_LetterToValue['I'] = 1;
871 m_LetterToValue['V'] = 5;
872 m_LetterToValue['X'] = 10;
873 m_LetterToValue['L'] = 50;
874 m_LetterToValue['C'] = 100;
875 m_LetterToValue['D'] = 500;
876 m_LetterToValue['M'] = 1000;
877
878 }
879
880 public static string Encode(int number)
881 {
882 var sb = new StringBuilder();
883 int remainder = number;
884
885 while (remainder > 0)
886 {
887 foreach (char c in m_MostToLeast)
888 {
889 if ((remainder - m_LetterToValue[c]) >= 0)
890 {
891 sb.Append(c);
892 remainder -= m_LetterToValue[c];
893 break;
894 }
895 }
896 }
897
898 // Now to optimizations
899 sb.Replace("VIIII", "IX");
900 sb.Replace("IIII", "IV");
901 sb.Replace("XXXX", "XL");
902 sb.Replace("LXXXX", "XC");
903 sb.Replace("CCCC", "CD");
904 sb.Replace("DCD", "CM");
905 sb.Replace("LXL", "XC");
906 sb.Replace("DCCCC", "CM");
907
908 return sb.ToString();
909 }
910 }
911
912 class SampleEventRaisingClass
913 {
914 public event EventHandler<EventArgs> SomethingHappened;
915
916 public void OnSomethingHappened()
917 {
918 // how many things can you count wrong in this one line of code?
919 SomethingHappened(this, EventArgs.Empty);
920
921 // HINT:
922
923 // 1. It shouldn't be public
924 // 2. should check for null:
925 // if (SomethingHappened != null)
926 // {
927 // SomethingHappened(this, EventArgs.Empty);
928 // }
929 // 3. But even that is wrong, there exists a possibility that
930 // SomethingHappened could become null after you checked it:
931 // var handler = SomethingHappened;
932 // if (handler != null)
933 // {
934 // handler(this, EventArgs.Empty);
935 // }
936
937 // What happens if your handlers throw an exception? What happens
938 // if your handler should be called on the UI thread, etc...
939
940 // Lots of things to think about, that's why you should really look
941 // at Juval's EventsHelper class:
942 // https://opensvn.csie.org/traccgi/BuckRogers/browser/trunk/Networking/EventsHelper.cs
943
944 // and use that everywhere instead of manually doing all these checks
945 // each time.
946
947 }
948 }
949}
|
|
Notes
|
|
http://www.moserware.com/
|
|
|