Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   Trading Reliability for Productivity

I started writing software in 1975. I did a fair amount of BASIC programming, and as I got more interested in hardware, I switched to assembly language. Over time, I switched to the C programming language because it allowed me access to hardware with a much higher level of abstraction, making my programming easier. My background is in writing operating systems’ code and platform/library code, so I always work hard to make my code as small and as fast as possible, because applications can only be as good as the operating system and libraries they consume.

In addition to creating small and fast code, I always focused on error recovery. When allocating memory (by using C++’s new operator or by calling malloc, HeapAlloc, VirtualAlloc, etc.), I would always check the return value to ensure that the memory I requested was actually given to me. And, if the memory request failed, I always had an alternate code path ensuring that the rest of the program’s state was unaffected and would let any of my callers know that I failed so that the calling code can take corrective measures too.


For some reason that I can’t quite explain, this attention to detail is not done when writing code for the .NET Framework. Getting an out-of-memory situation is always possible and yet I almost never see any code containing a catch block to recover from an OutOfMemoryException. In fact, I’ve even had some developers tell me that the CLR doesn’t let a program catch an OutOfMemory­ Exception. For the record, this is absolutely not true; you can catch this exception. In fact, there are many errors that are possible when executing managed code and I hardly ever see developers write code that attempts to recover from these potential failures. In this section, I’d like to point out some of the potential failures and why it has become culturally acceptable to ignore them. I’d also like to point out some of the significant problems that can occur when ignoring these failures and suggest some ways to help mitigate these problems.

Object-oriented programming allows developers to be very productive. A big part of this is com- posability which makes it easy to write, read and maintain code. Take this line of code, for example.

 



Boolean f = "Jeff".Substring(1, 1).ToUpper().EndsWith("E");

 



There is a big assumption being made with the preceding code: no errors occur. But, of course, errors are always possible, and so we need a way to handle those errors. This is what the exception handling constructs and mechanisms are all about and why we need them as opposed to having methods that return true/false or an HRESULT to indicate success/failure the way that Win32 and COM functions do.

In addition to code composability, we are productive due to all kinds of great features provided by our compilers. For example, the compiler implicitly:

■ Inserts optional parameters when calling a method.

 



■ Boxes value type instances.

 



■ Constructs/initializes parameter arrays.

 



■ Binds to members of dynamic variables and expressions.

 



■ Binds to extension methods.

 



■ Binds/invokes overloaded operators.

 



■ Constructs delegate objects.

 



■ Infers types when calling generic methods, declaring a local variable, and using a lambda expression.

■ Defines/constructs closure classes for lambda expressions and iterators.

 



■ Defines/constructs/initializes anonymous types and instances of them.

 



■ Rewrites code to support Language Integrated Queries (LINQs; query expressions and expression trees).


And, the CLR itself does all kinds of great things for developers to make our lives even easier. For example, the CLR implicitly:

■ Invokes virtual methods and interface methods.

 



■ Loads assemblies and JIT-compiles methods that can potentially throw FileLoadException, BadImageFormatException, InvalidProgramException, FieldAccessException, MethodAccessException, MissingFieldException, MissingMethodException, and VerificationException.

■ Transitions across AppDomain boundaries when accessing an object of a MarshalBy­ RefObject-derived type which can potentially throw AppDomainUnloadedException.

■ Serializes and deserializes objects when crossing an AppDomain boundary.

 



■ Causes thread(s) to throw a ThreadAbortException when Thread.Abort or

AppDomain.Unload is called.

 



■ Invokes Finalize methods after a garbage collection before objects have their memory reclaimed.

■ Creates type objects in the loader heap when using generic types.

 



■ Invokes a type’s static constructor potential throwing of TypeInitializationException.

 



■ Throws various exceptions, including OutOfMemoryException, DivideByZeroException, NullReferenceException, RuntimeWrappedException, TargetInvocationException, OverflowException, NotFiniteNumberException, ArrayTypeMismatchException, DataMisalignedException, IndexOutOfRangeException, InvalidCastException, RankException, SecurityException, and more.

And, of course, the .NET Framework ships with a massive class library that contains tens of thou- sands of types, each type encapsulating common, reusable functionality. There are types for building web form applications, web services, rich GUI applications, working with security, manipulation of images, speech recognition, and the list goes on and on. Any of this code could throw an exception, indicating failure. And future versions could introduce new exception types derived from existing exception types and now your catch blocks catch exception types that never existed before.

All of this stuff—object-oriented programming, compiler features, CLR features, and the enormous class library—is what makes the .NET Framework such a compelling software development platform.4 My point is that all of this stuff introduces points of failure into your code, which you have little con- trol over. As long as everything is working great, all is well: we write code easily, the code is easy to read and maintain. But, when something goes wrong, it is nearly impossible to fully understand what went wrong and why.

 



 



 
 

4 I should also add that Microsoft Visual Studio’s editor, IntelliSense support, code snippet support, templates, extensibil- ity system, debugging system, and various other tools also contribute to making the platform compelling for developers. However, I leave this out of the main discussion because it has no impact on the behavior of the code at run time.


Here is an example that should really help get my point across.

 



private static Object OneStatement(Stream stream, Char charToFind) {

return (charToFind + ": " + stream.GetType() + String.Empty + (stream.Position + 512M))

.Where(c=>c == charToFind).ToArray();

}

 



This slightly contrived method contains just one C# statement in it, but this statement does an awful lot of work. In fact, here is the Intermediate Language (IL) the C# compiler produced for this method. (I’ve put some lines in boldface italics that are potential points of failure due to implicit op- erations that are occurring.)

 



.method private hidebysig static object OneStatement(

class [mscorlib]System.IO.Stream stream, char charToFind) cil managed {

.maxstack 4

.locals init (

[0] class Program/<>c DisplayClass1 V_0, [1] object[] V_1)

IL_0000: newobj instance void Program/<>c DisplayClass1::.ctor()

IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldarg.1

IL_0008: stfld char Program/<>c DisplayClass1::charToFind IL_000d: ldc.i4.5


Date: 2016-03-03; view: 560


<== previous page | next page ==>
Nbsp;   Defining Your Own Exception Class | IL_006a: newobj instance
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.014 sec.)