美文网首页
70-483.C1O3-5.Manage program flo

70-483.C1O3-5.Manage program flo

作者: 小镭Ra | 来源:发表于2018-10-29 10:42 被阅读15次

    Note Links

    Objective 1.3: Implement program flow

    1.31 Working with Boolean expressions

    *If the runtime notices that the left part of your OR operation is true, it doesn’t have to evaluate the right part of your expression. This is called short-circuiting. So does AND operation.

    The Exclusive OR operator (XOR) returns true only when exactly one of the operands is true.

    1.32 Making decisions

    The null-coalescing operator

    The ?? operator is called the null-coalescing operator. It returns the left value if it’s not null; otherwise, the right operand.

    LISTING 1-59 Nesting the null-coalescing operator

    int? x = null; 
    int? z = null; 
    int y = x ?? 
            z ?? 
            -1;
    

    The conditional operator

    The ?: operator is called the conditional operator. If the expression is true, the first value is returned; otherwise, the second.

    1.33 Iterating across collections

    The for loop

    You can use multiple statements in each part of your for loop.

    LISTING 1-65 A for loop with multiple loop variables

    int[] values = { 1, 2, 3, 4, 5, 6 }; 
    for (int x = 0, y = values.Length - 1;  
        ((x < values.Length) && (y >= 0));  
        x++, y--) 
    { 
        Console.Write(values[x]); 
        Console.Write(values[y]); 
    } 
     
    // Displays 
    // 162534435261
    

    LISTING 1-66 A for loop with a custom increment

    int[] values = { 1, 2, 3, 4, 5, 6 }; 
    for (int index = 0; index < values.Length; index += 2) 
    { 
        Console.Write(values[index]); 
    } 
     
    // Displays 
    // 135
    

    The foreach loop

    In foreach loop The loop variable cannot be modified. You can make modifications to the object that the variable points to, but you can’t assign a new value to it.

    LISTING 1-72 Changing items in a foreach

    class Person 
    { 
        public string FirstName { get; set; } 
        public string LastName { get; set; } 
    } 
     
    void CannotChangeForeachIterationVariable() 
    { 
        var people = new List<Person> 
        { 
            new Person() { FirstName = "John", LastName = "Doe"}, 
            new Person() { FirstName = "Jane", LastName = "Doe"}, 
        }; 
     
        foreach (Person p in people) 
        { 
            p.LastName = "Changed"; // This is allowed 
            // p = new Person(); // This gives a compile error 
        } 
    }
    

    LISTING 1-73 The compiler-generated code for a foreach loop

    List<Person>.Enumerator e = people.GetEnumerator(); 
     
    try 
    { 
        Person v; 
        while (e.MoveNext()) 
        { 
            v = e.Current; 
       } 
    } 
    finally 
    { 
        System.IDisposable d = e as System.IDisposable; 
        if (d != null) d.Dispose(); 
    }
    

    If you change the value of e.Current to something else, the iterator pattern can’t determine what to do when e.MoveNext is called. This is why it’s not allowed to change the value of the iteration variable in a foreach statement.

    Jump statements

    You should try to avoid break and continue to improve the readability of your code. As a guideline, you should try to avoid using goto.

    MORE INFO ABOUT JUMP STATEMENTS

    Objective summary

    • Boolean expressions can use several operators: ==, !=, <, >, <=, >=, !. Those operators can be combined together by using AND (&&), OR (||) and XOR (^).
    • You can use the if-else statement to execute code depending on a specific condition.
    • The switch statement can be used when matching a value against a couple of options.
    • The for loop can be used when iterating over a collection where you know the number of iterations in advance.
    • A while loop can be used to execute some code while a condition is true; do-while
      should be used when the code should be executed at least once.
    • foreach can be used to iterate over collections.
    • Jump statements such as break, goto, and continue can be used to transfer control to another line of the program.

    Objective 1.4: Create and implement events and callbacks

    An event can be used to provide notifications.

    1.41 Understanding delegates

    In C#, delegates form the basic building blocks for events. A delegate is a type that defines a method signature.

    LISTING 1-75 Using a delegate

    public delegate int Calculate(int x, int y); 
     
    public int Add(int x, int y) { return x + y; } 
    public int Multiply(int x, int y) { return x * y; } 
     
    public void UseDelegate() 
    { 
        Calculate calc = Add; 
        Console.WriteLine(calc(3, 4)); // Displays 7 
     
        calc = Multiply; 
        Console.WriteLine(calc(3, 4)); // Displays 12 
    }
    

    Delegates can be nested in other types and they can then be used as
    a nested type.

    An instantiated delegate is an object; you can pass it around and give it as an argument to other methods.

    You can combine delegates together. This is called multi-casting.

    LISTING 1-76 A multicast delegate

    public void MethodOne()  
    {  
        Console.WriteLine("MethodOne");  
    } 
     
    public void MethodTwo() 
    { 
        Console.WriteLine("MethodTwo"); 
    } 
     
    public delegate void Del(); 
     
    public void Multicast() 
    { 
        Del d = MethodOne; 
        d += MethodTwo; 
     
        d(); 
    } 
    // Displays 
    // MethodOne 
    // MethodTwo
    

    All this is possible because delegates inherit from the System.MulticastDelegate class that in turn inherits from System.Delegate.

    You can find out how many methods a multicast delegate is going to call.

    int invocationCount = del.GetInvocationList().GetLength(0);
    

    When you assign a method to a delegate, the method signature does not have to match the delegate exactly. This is called covariance and contravariance. Covariance makes it possible that a method has a return type that is more derived than that defined in the delegate. Contravariance permits a method that has parameter types that are less derived than those in the delegate type.

    LISTING 1-77 Covariance with delegates

    public delegate TextWriter CovarianceDel(); 
     
    public StreamWriter MethodStream() { return null; } 
    public StringWriter MethodString() { return null; } 
     
    CovarianceDel del; 
    del = MethodStream; 
    del = MethodString;
    

    Because both StreamWriter and StringWriter inherit from TextWriter, you can use the CovarianceDel with both methods.

    LISTING 1-78 Contravariance with delegates

    void DoSomething(TextWriter tw) { } 
    public delegate void ContravarianceDel(StreamWriter tw); 
     
    ContravarianceDel del = DoSomething;
    

    MORE INFO ABOUT COVARIANCE AND CONTRAVARIANCE

    1.42 Using lambda expressions

    LISTING 1-79 Lambda expression to create a delegate

    Calculate calc = (x, y) => x + y; 
    Console.WriteLine(calc(3, 4)); // Displays 7 
    calc = (x, y) => x * y; 
    Console.WriteLine(calc(3, 4)); // Displays 12
    

    When reading this code, you can say go or goes to for the special lambda syntax. For example, the first lambda expression in Listing 1-79 is read as "x and y goes to adding x and y."

    Lambda functions have no specific name. Because of this, they are called anonymous functions.

    LISTING 1-80 Creating a lambda expression with multiple statements

    Calculate calc = 
        (x, y) => 
        { 
            Console.WriteLine("Adding numbers"); 
            return x + y; 
        }; 
     
    int result = calc(3, 4); 
    // Displays 
    // Adding numbers 
    

    .NET Framework has a couple of built-in delegate types that you can use when declaring delegates.

    Func<…> types can be found in the System namespace and they represent delegates that return a type and take 0 to 16 parameters.

    If you want a delegate type that doesn’t return a value, you can use the System.Action types.

    LISTING 1-81 Using the Action delegate

    Action<int, int> calc = (x, y) => 
    { 
        Console.WriteLine(x + y); 
    }; 
     
    calc(3, 4); // Displays 7
    

    Things start to become more complex when your lambda function starts referring to variables declared outside of the lambda expression (or to the this reference). Normally, when control leaves the scope of a variable, the variable is no longer valid. But what if a delegate refers to a local ariable and is then returned to the calling method? Now, the delegate has a longer life than the variable. To fix this, the compiler generates code that makes the life of the captured variable at least as long as the longest-living delegate. This is called a closure.

    1.43 Using events

    A popular design pattern (a reusable solution for a recurring problem) in application development is that of publish-subscribe. You can subscribe to an event and then you are notified when the publisher of the event raises a new event. This is used to establish loose coupling between components in an application.

    LISTING 1-82 Using an Action to expose an event

    public class Pub 
    { 
        public Action OnChange { get; set; } 
     
        public void Raise() 
        { 
            if (OnChange != null) 
            { 
                OnChange(); 
            } 
        } 
    } 
     
    public void CreateAndRaise() 
    { 
        Pub p = new Pub(); 
        p.OnChange += () => Console.WriteLine("Event raised to method 1"); 
        p.OnChange += () => Console.WriteLine("Event raised to method 2"); 
        p.Raise(); 
    }
    

    When calling CreateAndRaise, your code creates a new instance of Pub, subscribes to the event with two different methods and then raises the event by calling p.Raise. The Pub class is completely unaware of any subscribers. It just raises the event.

    If there would be no subscribers to an event, the OnChange property would be null. This is why the Raise method checks to see whether OnChange is not null.

    Weaknesses of LISTING 1-82:

    1. If you change the subscribe line for method 2 to the following, you would effectively remove the first subscriber by using = instead of +=:
    p.OnChange = () => Console.WriteLine("Event raised to method 2");
    
    1. Nothing prevents outside users of the class from raising the event. By just calling p.OnChange() every user of the class can raise the event to all subscribers.

    LISTING 1-83 Using the event keyword

    public class Pub 
    { 
        public event Action OnChange = delegate { }; 
     
        public void Raise() 
        { 
            OnChange(); 
        } 
    }
    

    CHANGES:

    1. You are no longer using a public property but a public field. Normally, this would be a step back. However, with the event syntax, the compiler protects your field from unwanted access.
    2. An event cannot be directly assigned to (with the = instead of +=) operator. So you don’t have the risk of someone removing all previous subscriptions, as with the delegate syntax.
    3. No outside users can raise your event. It can be raised only by code that’s part of the class that defined the event.
    4. Outside users of your class can’t set the event to null; only members of your class can.

    To follow the coding conventions in the .NET Framework. You should use the EventHandler or EventHandler<T> instead of using the Action type for your event.

    EventHandler is declared as the following delegate:

    public delegate void EventHandler(object sender, EventArgs e);
    

    By default, it takes a sender object and some event arguments. The sender is by convention the object that raised the event (or null if it comes from a static method). By using EventHandler<T>, you can specify the type of event arguments you want to use.

    LISTING 1-84 Custom event arguments

    public class MyArgs : EventArgs 
    { 
        public MyArgs(int value) 
        { 
            Value = value; 
        } 
     
        public int Value { get; set; } 
    } 
     
    public class Pub 
    { 
        public event EventHandler<MyArgs> OnChange = delegate { }; 
     
        public void Raise() 
        { 
            OnChange(this, new MyArgs(42)); 
        } 
    } 
     
    public void CreateAndRaise() 
    { 
        Pub p = new Pub(); 
     
        p.OnChange += (sender, e)  
            => Console.WriteLine("Event raised: {0}", e.Value); 
     
        p.Raise(); 
    }
    

    Although the event implementation uses a public field, you can still customize addition and removal of subscribers. This is called a custom event accessor.

    LISTING 1-85 Custom event accessor

    public class Pub 
    { 
        private event EventHandler<MyArgs> onChange = delegate { }; 
        public event EventHandler<MyArgs> OnChange 
        { 
            add 
            { 
                lock (onChange) 
                { 
                    onChange += value; 
                } 
            } 
            remove 
            { 
                lock (onChange) 
                { 
                    onChange -= value; 
                } 
            } 
        } 
     
        public void Raise() 
        { 
            onChange(this, new MyArgs(42)); 
        } 
    }
    
    

    It’s important to put a lock around adding and removing subscribers to make sure that the operation is thread safe.

    Events are not delegates, they are a convenient wrapper around delegates.

    LISTING 1-86 Exception when raising an event

    public class Pub 
    { 
        public event EventHandler OnChange = delegate { }; 
        public void Raise() 
        { 
            OnChange(this, EventArgs.Empty); 
        } 
    } 
     
    public void CreateAndRaise() 
    { 
        Pub p = new Pub(); 
     
        p.OnChange += (sender,e ) 
            => Console.WriteLine("Subscriber 1 called"); 
     
        p.OnChange += (sender, e) 
            => { throw new Exception(); }; 
     
        p.OnChange += (sender,e ) 
            => Console.WriteLine("Subscriber 3 called"); 
        p.Raise(); 
    }
    // Displays 
    // Subscriber 1 called
    

    LISTING 1-87 Manually raising events with exception handling

    public class Pub
    { 
        public event EventHandler OnChange = delegate { }; 
        public void Raise() 
        { 
            var exceptions = new List<Exception>(); 
     
            foreach (Delegate handler in OnChange.GetInvocationList()) 
            { 
                try 
                { 
                    handler.DynamicInvoke(this, EventArgs.Empty); 
                } 
                catch (Exception ex) 
                { 
                    exceptions.Add(ex); 
                } 
            } 
     
            if (exceptions.Any()) 
            { 
                throw new AggregateException(exceptions); 
            } 
        } 
    } 
     
    public void CreateAndRaise() 
    { 
        Pub p = new Pub(); 
     
        p.OnChange += (sender, e) 
            => Console.WriteLine("Subscriber 1 called"); 
     
        p.OnChange += (sender, e) 
            => { throw new Exception(); }; 
     
        p.OnChange += (sender, e) 
            => Console.WriteLine("Subscriber 3 called"); 
     
        try 
        { 
            p.Raise(); 
        } 
        catch (AggregateException ex) 
        { 
            Console.WriteLine(ex.InnerExceptions.Count); 
        } 
    } 
     
    // Displays 
    // Subscriber 1 called 
    // Subscriber 3 called 
    // 1
    

    Objective summary

    • Delegates are a type that defines a method signature and can contain a reference to a method.
    • Delegates can be instantiated, passed around, and invoked.
    • Lambda expressions, also known as anonymous methods, use the => operator and form a compact way of creating inline methods.
    • Events are a layer of syntactic sugar on top of delegates to easily implement the publish-subscribe pattern.
    • Events can be raised only from the declaring class. Users of events can only remove and add methods the invocation list.
    • You can customize events by adding a custom event accessor and by directly using the underlying delegate type.

    Objective 1.5: Implement exception handling

    1.51 Handling exceptions

    When an error occurs somewhere in an application, an exception is raised.

    An exception not only has a user-friendly message but it also contains the location in which the error happened and it can even store extra data, such as an address to a page that offers some help.

    If an exception goes unhandled, it will cause the current process to terminate.

    LISTING 1-88 Parsing an invalid number

    namespace ExceptionHandling 
    { 
        public static class Program 
        { 
            public static void Main() 
            {             
                string s = "NaN"; 
                int i = int.Parse(s); 
            } 
        } 
    } 
    // Displays 
    // Unhandled Exception: System.FormatException: Input string was not in a correct format. 
    //   at System.Number.StringToNumber(String str, NumberStyles options,  
    //        NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal) 
    //   at System.Number.ParseInt32(String s, NumberStyles style,  
    //        NumberFormatInfo info) 
    //   at System.Int32.Parse(String s) 
    //   at ExceptionHandling.Program.Main() in c:\Users\Wouter\Documents\  
    //   Visual Studio 2012\Projects\ExamRefProgrammingInCSharp\Chapter1\Program.cs:line 9
    

    LISTING 1-89 Catching a FormatException

    using System; 
    namespace ExceptionHandling 
    { 
        public static class Program 
        { 
            public static void Main() 
            { 
                while (true) 
                { 
                    string s = Console.ReadLine(); 
      
                    if (string.IsNullOrWhiteSpace(s)) break; 
      
                    try 
                    { 
                        int i = int.Parse(s); 
                        break; 
                    } 
                    catch (FormatException) 
                    { 
                        Console.WriteLine("{0} is not a valid number. Please try again", s); 
                    } 
                } 
            } 
        } 
    }
    

    You need to surround the code that can potentially throw an exception with a try statement. Following the try statement, you can add several different catch blocks. A catch block can specify the type of the exception it wants to catch. All exceptions in the .NET Framework inherit from System.Exception.

    The catch blocks should be specified as most-specific to least-specific because this is the order in which the runtime will examine them. When an exception is thrown, the first matching catch block will be executed. If no matching catch block can be found, the exception will fall through.

    LISTING 1-90 Catching different exception types

    try 
    { 
        int i = int.Parse(s); 
    } 
    catch (ArgumentNullException) 
    {
        Console.WriteLine("You need to enter a value"); 
    } 
    catch (FormatException) 
    { 
        Console.WriteLine("{0} is not a valid number. Please try again", s); 
    }
    

    In C# 1, you could also use a catch block without an exception type. This could be used to catch exceptions that were thrown from other languages like C++ that don’t inherit from System.Exception (in C++ you can throw exceptions of any type). Nowadays, each exception that doesn’t inherit from System.Exception is automatically wrapped in a System.Runtime.CompilerServices.RuntimeWrappedException. Since this exception inherits from System.Exception, there is no need for the empty catch block anymore.

    It’s important to make sure that your application is in the correct state when the catch block finishes. This could mean that you need to revert changes that your try block made before the exception was thrown.

    The finally block will execute whether an exception happens or not.

    LISTING 1-91 Using a finally block

    using System; 
     
    namespace ExceptionHandling 
    { 
        public static class Program 
        { 
            public static void Main() 
            { 
                string s = Console.ReadLine(); 
      
                try 
                { 
                    int i = int.Parse(s); 
                } 
                catch (ArgumentNullException) 
                { 
                    Console.WriteLine("You need to enter a value"); 
                } 
                catch (FormatException)
                { 
                    Console.WriteLine("{0} is not a valid number. Please try again", s); 
                } 
                finally 
                {
                    Console.WriteLine("Program complete."); 
                } 
            } 
        } 
    } 
     
    // Displays 
    // a 
    // a is not a valid number. Please try again 
    // Program complete.
    

    When the try block goes into an infinite loop, or in situations such as a power outage, a finally block won’t run.

    Preventing the finally block from running can be achieved by using Environment.FailFast. When this method is called, the message (and optionally the exception) are written to the Windows application event log, and the application is terminated.

    LISTING 1-92 Using Environment.FailFast

    using System; 
    namespace ExceptionHandling 
    { 
        public static class Program 
        { 
            public static void Main() 
            { 
                string s = Console.ReadLine(); 
      
                try 
                { 
                    int i = int.Parse(s); 
                    if (i == 42) Environment.FailFast("Special number entered"); 
                } 
                finally 
                { 
                    Console.WriteLine("Program complete."); 
                } 
            } 
        } 
    } 
    

    The line Program Complete won’t be executed if 42 is entered. Instead the application shuts down immediately.

    TABLE 1-3 System.Exception properties

    Name Description
    StackTrace A string that describes all the methods that are currently in execution. This gives you a way of tracking which method threw the exception and how that method was reached.
    InnerException When a new exception is thrown because another exception happened, the two are linked together with the InnerException property.
    Message A (hopefully) human friendly message that describes the exception.
    HelpLink A Uniform Resource Name (URN) or uniform resource locater (URL) that points to a help file.
    HResult A 32-bit value that describes the severity of an error, the area in which the exception happened and a unique number for the exception This value is used only when crossing managed and native boundaries.
    Source The name of the application that caused the error. If the Source is not explicitly set, the name of the assembly is used.
    TargetSite Contains the name of the method that caused the exception. If this data is not available, the property will be null.
    Data A dictionary of key/value pairs that you can use to store extra data for your exception. This data can be read by other catch blocks and can be used to control the processing of the exception.

    When using a catch block, you can use both an exception type and a named identifier. This way, you effectively create a variable that will hold the exception for you so you can inspect its properties.

    LISTING 1-93 Inspecting an exception

    using System; 
     
    namespace ExceptionHandling 
    { 
        public static class Program 
        { 
            public static void Main() 
            { 
                try 
                { 
                    int i = ReadAndParse(); 
                    Console.WriteLine("Parsed: {0}", i); 
                } 
                catch (FormatException e) 
                { 
                    Console.WriteLine("Message: {0}",e.Message); 
                    Console.WriteLine("StackTrace: {0}", e.StackTrace); 
                    Console.WriteLine("HelpLink: {0}", e.HelpLink); 
                    Console.WriteLine("InnerException: {0}", e.InnerException); 
                    Console.WriteLine("TargetSite: {0}", e.TargetSite); 
                    Console.WriteLine("Source: {0}", e.Source); 
                }
            } 
      
            private static int ReadAndParse() 
            { 
                string s = Console.ReadLine(); 
                int i = int.Parse(s); 
                return i; 
            } 
        } 
    } 
     
    //Displays  
    //Message: Input string was not in a correct format. 
    //StackTrace:    at System.Number.StringToNumber(String str, NumberStyles options, 
    // NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal) 
    //   at System.Number.ParseInt32(String s, NumberStyles style, 
    //        NumberFormatInfo info) 
    //   at System.Int32.Parse(String s) 
    //   at ExceptionHandling.Program.ReadAndParse() in  
    //      c:\Users\Wouter\Documents\Visual Studio 2012\Projects\ 
    //        ExamRefProgrammingInCSharp\Chapter1\Program.cs:line 27 
    //   at ExceptionHandling.Program.Main() in c:\Users\Wouter\Documents\ 
    //        Visual Studio 2012\Projects\ExamRefProgrammingInCSharp\ 
    //        Chapter1\Program.cs:line 10 
    // HelpLink: 
    // InnerException: 
    // TargetSite: Void StringToNumber(System.String, System.Globalization.NumberStyles 
    // , NumberBuffer ByRef, System.Globalization.NumberFormatInfo, Boolean) 
    // Source: mscorlib
    

    It’s important to make sure that your finally block does not cause any exceptions.

    You should only catch an exception when you can resolve the issue or when you want to log the error. Because of this, it’s important to avoid general catch blocks at the lower layers of your application. This way, you could accidentally swallow an important exception without even knowing that it happened. Logging should also be done somewhere higher up in your application. That way, you can avoid logging duplicate errors at multiple layers in your application.

    1.52 Throwing exceptions

    LISTING 1-94 Throwing an ArgumentNullException

    public static string OpenAndParse(string fileName) 
    { 
        if (string.IsNullOrWhiteSpace(fileName)) 
            throw new ArgumentNullException("fileName", "Filename is required"); 
     
        return File.ReadAllText(fileName); 
    }
    

    You should not try to reuse exception objects. Each time you throw an exception, you should create a new one, especially when working in a multithreaded environment, the stack trace of your exception can be changed by another thread.

    When catching an exception, you can choose to rethrow the exception in three ways:

    • Use the throw keyword without an identifier
      rethrows the exception without modifying the call stack. It should be used when you don’t want any modifications to the exception.
    • Use the throw keyword with the original exception
      reset the call stack to the current location in code. So you can’t see where the exception originally came from, and it is harder to debug the error.
    • Use the throw keyword with a new exception
      It can be useful when you want to raise another exception to the caller of your code.

    LISTING 1-95 Rethrowing an exception

    try 
    { 
        SomeOperation(); 
    } 
    catch (Exception logEx) 
    { 
        Log(logEx); 
        throw; // rethrow the original exception 
    }
    

    LISTING 1-96 Throwing a new exception that points to the original one

    try 
    { 
        ProcessOrder(); 
    } 
    catch (MessageQueueException ex) 
    { 
        throw new OrderProcessingException("Error while processing order", ex); 
    }
    

    ExceptionDispatchInfo.Throw method in the System.Runtime.ExceptionServices namespace can be used to throw an exception and preserve the original stack trace.

    LISTING 1-97 Using ExceptionDispatchInfo.Throw

    ExceptionDispatchInfo possibleException = null; 
     
    try 
    { 
        string s = Console.ReadLine(); 
        int.Parse(s); 
    } 
    catch (FormatException ex) 
    { 
        possibleException =  ExceptionDispatchInfo.Capture(ex); 
    } 
     
    if (possibleException != null) 
    { 
        possibleException.Throw(); 
    }
    

    // Displays
    // Unhandled Exception: System.FormatException:
    // Input string was not in a correct format.
    // at System.Number.StringToNumber(String str, NumberStyles options,
    // NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
    // at System.Number.ParseInt32(String s, NumberStyles style,
    // NumberFormatInfo info)
    // at System.Int32.Parse(String s)
    // at ExceptionHandling.Program.Main() in c:\Users\Wouter\Documents\
    // Visual Studio 2012\Projects\ExamRefProgrammingInCSharp\Chapter1\
    // Program.cs:line 17
    //--- End of stack trace from previous location where exception was thrown ---
    // at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    // at ExceptionHandling.Program.Main() in c:\Users\Wouter\Documents\
    // Visual Studio 2012\Projects\ExamRefProgrammingInCSharp\Chapter1\
    // Program.cs:line 6

    This feature can be used when you want to catch an exception in one thread and throw it on another thread.

    Exception handling changes the normal expected flow of your program. This makes it harder to read and maintain code that uses exceptions.

    Using exceptions also incurs a slight performance hit. Because the runtime has to search all outer catch blocks until it finds a matching block, and when it doesn’t, has to look if a debugger is attached, it takes slightly more time to handle.

    When you need to throw an exception of your own, it’s important to know which exceptions are already defined in the .NET Framework.

    Some exceptions are thrown only by the runtime. You shouldn’t use those exceptions from your own code.

    TABLE 1-4 Runtime exceptions in the .NET Framework

    Name Description
    ArithmeticException A base class for other exceptions that occur during arithmetic operations.
    ArrayTypeMismatchException Thrown when you want to store an incompatible element inside an array.
    DivideByZeroException Thrown when you try to divide a value by zero.
    IndexOutOfRangeException Thrown when you try to access an array with an index that’s less than zero or greater than the size of the array.
    InvalidCastException Thrown when you try to cast an element to an incompatible type.
    NullReferenceException Thrown when you try to reference an element that’s null.
    OutOfMemoryException Thrown when creating a new object fails because the CLR doesn’t have enough memory available.
    OverflowException Thrown when an arithmetic operation overflows in a checked context.
    StackOverflowException Thrown when the execution stack is full. This can happen in a recursive operation that doesn’t exit.
    TypeInitializationException Thrown when a static constructor throws an exception that’s goes unhandled.

    TABLE 1-5 Popular exceptions in the .NET Framework

    Name Description
    Exception The base class for all exceptions. Try avoiding throwing and catching this exception because it’s too generic.
    ArgumentException Throw this exception when an argument to your method is invalid.
    ArgumentNullException A specialized form of ArgumentException that you can throw when one of your arguments is null and this isn’t allowed.
    ArgumentOutOfRangeException A specialized form of ArgumentException that you can throw when an argument is outside the allowable range of values.
    FormatException Throw this exception when an argument does not have a valid format.
    InvalidOperationException Throw this exception when a method is called that’s invalid for the object’s current state.
    NotImplementedException This exception is often used in generated code where a method has not been implemented yet.
    NotSupportedException Throw this exception when a method is invoked that you don’t support.
    ObjectDisposedException Throw when a user of your class tries to access methods when Dispose has already been called.

    You should avoid directly using the Exception base class both when catching and throwing exceptions. Instead you should try to use the most specific exception available.

    1.53 Creating custom exceptions

    A custom exception is especially useful when developers working with your code are aware of those exceptions and can handle them in a more specific way than the framework exceptions.

    A custom exception should inherit from System.Exception. You need to provide at least a parameterless constructor. It’s also a best practice to add a few other constructors: one that takes a string, one that takes both a string and an exception, and one for serialization.

    LISTING 1-98 Creating a custom exception

    [Serializable] 
    public class OrderProcessingException : Exception, ISerializable 
    { 
        public OrderProcessingException(int orderId) 
        { 
            OrderId = orderId; 
            this.HelpLink = "http://www.mydomain.com/infoaboutexception"; 
        } 
        public OrderProcessingException(int orderId, string message) 
            : base(message) 
        { 
            OrderId = orderId; 
            this.HelpLink = "http://www.mydomain.com/infoaboutexception"; 
        } 
     
        public OrderProcessingException(int orderId, string message,  
                                         Exception innerException) 
            : base(message, innerException) 
        { 
            OrderId = orderId; 
            this.HelpLink = "http://www.mydomain.com/infoaboutexception"; 
        } 
     
        protected OrderProcessingException(SerializationInfo info, StreamingContext context) 
        { 
            OrderId = (int)info.GetValue("OrderId", typeof(int)); 
        } 
     
        public int OrderId { get; private set; } 
     
        public void GetObjectData(SerializationInfo info, StreamingContext context) 
        { 
            info.AddValue("OrderId", OrderId, typeof(int)); 
        } 
    }
    

    By convention, you should use the Exception suffix in naming all your custom exceptions.

    It’s also important to add the Serializable attribute, which makes sure that your exception can be serialized and works correctly across application domains (for example, when a web service returns an exception).

    You should never inherit from System.ApplicationException. The original idea was that all C# runtime exceptions should inherit from System.Exception and all custom exceptions from System.ApplicationException. However, because the .NET Framework doesn’t follow this pattern, the class became useless and lost its meaning.

    Objective summary

    • In the .NET Framework, you should use exceptions to report errors instead of error codes.
    • Exceptions are objects that contain data about the reason for the exception.
    • You can use a try block with one or more catch blocks to handle different types of exceptions.
    • You can use a finally block to specify code that should always run after, whether or not an exception occurred.
    • You can use the throw keyword to raise an exception.
    • You can define your own custom exceptions when you are sure that users of your code will handle it in a different way. Otherwise, you should use the standard .NET Framework exceptions.

    Chapter summary

    • Multithreading can help you create programs that are responsive and scalable. You can use the TPL, the Parallel class, and PLINQ for this. The new async/await keywords help you write asynchronous code.
    • In a multithreaded environment, it’s important to manage synchronization of shared data. You can do this by using the lock statement.
    • C# offers statements for making decisions—if, switch, conditional operator (?) and null-coalescing operator (??)—iterating (for, foreach, while, do-while), and jump statements (break, continue, goto, return and throw).
    • Delegates are objects that point to a method and can be used to invoke the method. Lambda expressions are a shorthand syntax for creating anonymous methods inline.
    • Events are a layer on top of delegates that help you with creating a publish-subscribe architecture.
    • Exceptions are the preferred way to work with errors in the .NET Framework. You can throw exceptions, catch them, and run code in a finally block.

    Note Links

    相关文章

      网友评论

          本文标题:70-483.C1O3-5.Manage program flo

          本文链接:https://www.haomeiwen.com/subject/gttbtqtx.html