View
226
Download
0
Category
Preview:
Citation preview
Effective .NET Framework based Effective .NET Framework based
Development:Development: Exception Exception Handing and Memory Handing and Memory
ManagementManagement
Brad AbramsBrad AbramsLead Program ManagerLead Program ManagerCommon Language Runtime TeamCommon Language Runtime TeamMicrosoft Corporation Microsoft Corporation
brada@microsoft.com http://blogs.msdn.com/brada
AgendaAgenda
Exception Exception ““Cleaner, more elegant, and wrong.” Cleaner, more elegant, and wrong.”
Raymond Chen Raymond Chen ((http://blogs.msdn.com/oldnewthing/)http://blogs.msdn.com/oldnewthing/)
Memory ManagementMemory Management“I'm addicted to deterministic destruction“I'm addicted to deterministic destruction” ”
Chris Sells Chris Sells ((http://www.sellsbrothers.com/)http://www.sellsbrothers.com/)
Exception Handling Exception Handling QuestionsQuestions
Throwing an exceptionThrowing an exception What exception to throw?What exception to throw? What data to include in the exception?What data to include in the exception?
Managing resourcesManaging resources What resources need to be cleaned up?What resources need to be cleaned up? When should they be cleaned up?When should they be cleaned up?
Catching an ExceptionCatching an Exception When to catch an exception?When to catch an exception? How to report the error?How to report the error? What to do with the control follow: terminate What to do with the control follow: terminate
the app, abort the transaction, ignore the the app, abort the transaction, ignore the exception, disable some functionality, etc.?exception, disable some functionality, etc.?
When to Throw?When to Throw?
Exceptions rather than error codesExceptions rather than error codes Robust: failures get noticedRobust: failures get noticed
Your method is defined to do Your method is defined to do something…something… If it succeeds in performing its purpose, If it succeeds in performing its purpose,
returnreturn If it fails to do what it was written to do, If it fails to do what it was written to do,
throw an exceptionthrow an exception
What to throw?What to throw?
Use or subclass existing exceptions Use or subclass existing exceptions if at all possibleif at all possible
Only create separate classes if you Only create separate classes if you think developers will handle the think developers will handle the exception differentlyexception differently
try { //some operation}catch (FileNotFoundException fe) { //do some set of work}catch (DriveNotFoundException be) { //do some other set of work}
try { //some operation}catch (FileNotFoundException fe) { //do some set of work}catch (DriveNotFoundException be) { //do some other set of work}
Throwing an ExceptionThrowing an Exception
Do not just map error codes on to a Do not just map error codes on to a single exception with an error code single exception with an error code property (property (e.g.e.g., the WMIException), the WMIException) Use separate exception typesUse separate exception types
Error MessagesError Messages Consider localization Consider localization Use a complete sentence (end in a Use a complete sentence (end in a
period)period) Don’t expose privacy related Don’t expose privacy related
information (such as file paths)information (such as file paths)
PerformancePerformance Minimize the number of exceptions Minimize the number of exceptions
you throw in your API’s success code-you throw in your API’s success code-pathspaths You don’t pay for exceptions until you You don’t pay for exceptions until you
throw in managed codethrow in managed code Throwing exceptions degrades Throwing exceptions degrades
performanceperformance Perf counters tell you exactly how many Perf counters tell you exactly how many
exceptions your application is throwingexceptions your application is throwing Only an issue when using exceptions as Only an issue when using exceptions as
control flowcontrol flow Consider providing a way to avoid an Consider providing a way to avoid an
exception being thrownexception being thrown
Performance (continued)Performance (continued)
int i;try { i = Int32.Parse(“123”);} catch (FormatException ) { Console.WriteLine (“Invalid”);}
int i;try { i = Int32.Parse(“123”);} catch (FormatException ) { Console.WriteLine (“Invalid”);}
int i;if (!Int32.TryParse (“123”, out i)) { Console.Writeline(“Invalid”);}
int i;if (!Int32.TryParse (“123”, out i)) { Console.Writeline(“Invalid”);}
Managing ResourcesManaging Resources
You should use try..finally 10 times You should use try..finally 10 times as often as try..catchas often as try..catch Catches eat exceptions making it hard Catches eat exceptions making it hard
to debugto debug Finally allows you to clean up, but let Finally allows you to clean up, but let
the exception continuethe exception continue
Managing ResourcesManaging Resources
You may catch exceptions to re-throw them with a You may catch exceptions to re-throw them with a clearer nameclearer name Typical at an “API” boundaryTypical at an “API” boundary
Always nest the underlying exceptionAlways nest the underlying exception Catch-and-rethrow has many of the benefits as Catch-and-rethrow has many of the benefits as
try..finallytry..finally But, be aware of debugging issues with catch..throw new() But, be aware of debugging issues with catch..throw new()
and catch..throw;and catch..throw; Generally, cleanup code should go in finalizerGenerally, cleanup code should go in finalizer
try {. . .}catch (DivisionByZeroException e) { // do clean up work throw new BetterException (message, e);}
try {. . .}catch (DivisionByZeroException e) { // do clean up work throw new BetterException (message, e);}
Catching ExceptionsCatching Exceptions
Do not catch and eat exceptionsDo not catch and eat exceptions Exceptions should be handled only where Exceptions should be handled only where
there is enough context to do the right thingthere is enough context to do the right thing That generally means exceptions should be That generally means exceptions should be
caught as high in the application as possiblecaught as high in the application as possible Mistake – catch the exception, report the Mistake – catch the exception, report the
error and rethrow it. error and rethrow it. Only catch where you can handle itOnly catch where you can handle it
Mistake – catch the exception, turn it into Mistake – catch the exception, turn it into a bool pass/fail and return the bool a bool pass/fail and return the bool
Catching ExceptionsCatching Exceptions Consider including a try/catch at the top Consider including a try/catch at the top
of a thread’s stack if the error can be of a thread’s stack if the error can be handled properlyhandled properly Unhandled exceptions at the top of the main Unhandled exceptions at the top of the main
thread will terminate the appthread will terminate the app In 2.0, unhandled exceptions at the top of the In 2.0, unhandled exceptions at the top of the
stack on any thread will terminate the appstack on any thread will terminate the app But avoid catch blocks in finalizersBut avoid catch blocks in finalizers
Be aware: In many cases it is “appropriate” to Be aware: In many cases it is “appropriate” to let the app terminate let the app terminate
Be aware of (but ignore) exceptions that Be aware of (but ignore) exceptions that don’t inherit from System.Exceptiondon’t inherit from System.Exception Allowed in V1.0\V1.1, likely addressed in V2.0Allowed in V1.0\V1.1, likely addressed in V2.0
catch (Exception e) is catch (Exception e) is your friendyour friend
Myth: Catching Exception is evilMyth: Catching Exception is evil This is motivated by a desire to avoid catching This is motivated by a desire to avoid catching
low level exceptions such as low level exceptions such as OutOfMemoryException, and OutOfMemoryException, and StackOverflowExceptionStackOverflowException
Do catch every exception you should handle Do catch every exception you should handle Don’t attempt to catch every exception a Don’t attempt to catch every exception a
method could throwmethod could throw Its ugly, version brittle, and difficult to testIts ugly, version brittle, and difficult to test
Catch what you need to handle, let the rest Catch what you need to handle, let the rest passpass
AgendaAgenda
Exception Exception ““Cleaner, more elegant, and wrong.” Cleaner, more elegant, and wrong.”
Raymond Chen Raymond Chen ((http://blogs.msdn.com/oldnewthing/)http://blogs.msdn.com/oldnewthing/)
Memory ManagementMemory Management““I'm addicted to deterministic I'm addicted to deterministic
destruction” destruction” Chris Sells Chris Sells ((http://www.sellsbrothers.com/)http://www.sellsbrothers.com/)
Memory ManagementMemory Management
The GC does an excellent job managing The GC does an excellent job managing “managed” memory“managed” memory
GC doesn’t manage external resources GC doesn’t manage external resources (DB connections, HWnds, (DB connections, HWnds, etc.etc.))
Generational Mark-and-sweep garbage Generational Mark-and-sweep garbage collection means non-deterministic collection means non-deterministic finalizationfinalization Exact time of finalization is unspecifiedExact time of finalization is unspecified Order of finalization is unspecifiedOrder of finalization is unspecified Thread is unspecifiedThread is unspecified
Resource Management Resource Management
If you are encapsulating external If you are encapsulating external resources:resources: Add a finalizer (C# destructor) to guarantee Add a finalizer (C# destructor) to guarantee
the resource will eventually be freedthe resource will eventually be freed Provide developers an explicit way to free Provide developers an explicit way to free
external resourcesexternal resources Formalized in the IDisposable interfaceFormalized in the IDisposable interface
Signals to users they need to explicitly Dispose of Signals to users they need to explicitly Dispose of instancesinstances
Enables C# and VB (2005) Enables C# and VB (2005) usingusing support support
FinalizersFinalizers Object.Finalize() is not accessible Object.Finalize() is not accessible
in C#in C# VERY different than C++’s VERY different than C++’s
destructorsdestructorspublic class Resource{ ~Resource() { ... }}
public class Resource{ ~Resource() { ... }}
public class Resource{ protected override void Finalize() { try { ... } finally { base.Finalize(); } }}
public class Resource{ protected override void Finalize() { try { ... } finally { base.Finalize(); } }}
Finalizers (2)Finalizers (2) Only implement Finalize on objects Only implement Finalize on objects
that need finalizationthat need finalization Finalization is only appropriate for Finalization is only appropriate for
cleanup of unmanaged resourcescleanup of unmanaged resources Keeps objects alive an order of Keeps objects alive an order of
magnitude longermagnitude longer Free any external resources you own Free any external resources you own
in your Finalize methodin your Finalize method Do not throw exceptions in finalizersDo not throw exceptions in finalizers
The rest of your finalizer will not run The rest of your finalizer will not run Check out Critical Finalizers in 2.0Check out Critical Finalizers in 2.0
Finalizers (3)Finalizers (3)
Do not block or wait in finalizersDo not block or wait in finalizers All finalization for that process could be All finalization for that process could be
stoppedstopped Only release resources that are held Only release resources that are held
onto by this instanceonto by this instance Do not reference other instancesDo not reference other instances Will be called on one or more Will be called on one or more
different threadsdifferent threads
Dispose PatternDispose Pattern
Implement the dispose pattern Implement the dispose pattern whenever you have a finalizerwhenever you have a finalizer Gives developers explicit controlGives developers explicit control
Free any disposable resources your Free any disposable resources your type owns in the type owns in the Dispose()Dispose() method method Not just the external resourcesNot just the external resources Propagate calls to Propagate calls to Dispose()Dispose() through through
containment hierarchiescontainment hierarchies
Dispose Pattern (2)Dispose Pattern (2) Suppress finalization once Suppress finalization once Dispose()Dispose() has has
been calledbeen called Dispose()Dispose() should be callable multiple should be callable multiple
times without throwing an exceptiontimes without throwing an exception The method will do nothing after the first callThe method will do nothing after the first call After After Dispose()Dispose() is called other methods on is called other methods on
the class can throw ObjectDisposedException the class can throw ObjectDisposedException Do not assume that Do not assume that Dispose() Dispose() will be calledwill be called
For unmanaged cleanup have a finalizer as wellFor unmanaged cleanup have a finalizer as well Do call your base class’s Do call your base class’s Dispose(bool)Dispose(bool)
method if it implements IDisposablemethod if it implements IDisposable
Implementing Implementing IDisposableIDisposable
public class Resource: IDisposable { private bool disposed = false; pubic int GetValue () { if (disposed) throw new ObjectDisposedException(); // do work } public void Dispose() { if (disposed) return; Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { // Dispose dependent objects disposed = true; } // Free unmanaged resources } ~Resource() { Dispose(false); }}
public class Resource: IDisposable { private bool disposed = false; pubic int GetValue () { if (disposed) throw new ObjectDisposedException(); // do work } public void Dispose() { if (disposed) return; Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { // Dispose dependent objects disposed = true; } // Free unmanaged resources } ~Resource() { Dispose(false); }}
Using StatementUsing Statement Acquire, Execute, Release patternAcquire, Execute, Release pattern Works with any IDisposable objectWorks with any IDisposable object
Data access classes, streams, text Data access classes, streams, text readers and writers, network classes, readers and writers, network classes, etc.etc.using (Resource res = new Resource()) {
res.DoWork();}
using (Resource res = new Resource()) { res.DoWork();} Resource res = new Resource(...);
try { res.DoWork();}finally { if (res != null)
((IDisposable)res).Dispose();}
Resource res = new Resource(...);try { res.DoWork();}finally { if (res != null)
((IDisposable)res).Dispose();}
Using StatementUsing Statement Acquire, Execute, Release patternAcquire, Execute, Release pattern Works with any IDisposable objectWorks with any IDisposable object
Data access classes, streams, text Data access classes, streams, text readers and writers, network classes, readers and writers, network classes, etc.etc.Using res As Resource = New Resource ()
res.DoWork()End Using
Using res As Resource = New Resource () res.DoWork()End Using Dim res As New Resource()
Try res.DoWork()Finally If res IsNot Nothing Then CType(res, IDisposable).Dispose() End IfEnd Try
Dim res As New Resource()Try res.DoWork()Finally If res IsNot Nothing Then CType(res, IDisposable).Dispose() End IfEnd Try
Using Statement (2)Using Statement (2) Can you find the “bug” in this code?Can you find the “bug” in this code? Will input and output always be Will input and output always be
closed?closed?static void Copy(string sourceName, string destName) { Stream input = File.OpenRead(sourceName); Stream output = File.Create(destName); byte[] b = new byte[65536]; int n; while ((n = input.Read(b, 0, b.Length)) != 0) { output.Write(b, 0, n); } output.Close(); input.Close();}
static void Copy(string sourceName, string destName) { Stream input = File.OpenRead(sourceName); Stream output = File.Create(destName); byte[] b = new byte[65536]; int n; while ((n = input.Read(b, 0, b.Length)) != 0) { output.Write(b, 0, n); } output.Close(); input.Close();}
Using Statement (3)Using Statement (3) static void Copy(string sourceName, string destName) { Stream input = File.OpenRead(sourceName); Stream output = File.Create(destName); try { byte[] b = new byte[65536]; int n; while ((n = input.Read(b, 0, b.Length)) != 0) { output.Write(b, 0, n); } } finally { output.Close(); input.Close(); }}
static void Copy(string sourceName, string destName) { Stream input = File.OpenRead(sourceName); Stream output = File.Create(destName); try { byte[] b = new byte[65536]; int n; while ((n = input.Read(b, 0, b.Length)) != 0) { output.Write(b, 0, n); } } finally { output.Close(); input.Close(); }}
Using Statement (4)Using Statement (4)static void Copy(string sourceName, string destName) { Stream input = File.OpenRead(sourceName); try { Stream output = File.Create(destName); try { byte[] b = new byte[65536]; int n; while ((n = input.Read(b, 0, b.Length)) != 0) { output.Write(b, 0, n); } } finally { output.Close(); } } finally { input.Close(); }}
static void Copy(string sourceName, string destName) { Stream input = File.OpenRead(sourceName); try { Stream output = File.Create(destName); try { byte[] b = new byte[65536]; int n; while ((n = input.Read(b, 0, b.Length)) != 0) { output.Write(b, 0, n); } } finally { output.Close(); } } finally { input.Close(); }}
Using Statement (4)Using Statement (4) Code is correct and much more Code is correct and much more
readable with using statementsreadable with using statements Types should implement Types should implement
IDisposable to take advantage of IDisposable to take advantage of this supportthis support
static void Copy(string sourceName, string destName) { using (Stream input = File.OpenRead(sourceName)) using (Stream output = File.Create(destName)) { byte[] b = new byte[65536]; int n; while ((n = input.Read(b, 0, b.Length)) != 0) { output.Write(b, 0, n); } }}
static void Copy(string sourceName, string destName) { using (Stream input = File.OpenRead(sourceName)) using (Stream output = File.Create(destName)) { byte[] b = new byte[65536]; int n; while ((n = input.Read(b, 0, b.Length)) != 0) { output.Write(b, 0, n); } }}
Resource Management Resource Management 2.0 Feature: 2.0 Feature:
MemoryPressureMemoryPressure GC.AddMemoryPressure ( int GC.AddMemoryPressure ( int
pressure )pressure ) Useful when you have a disproportionate Useful when you have a disproportionate
ratio of managed, to unmanaged ratio of managed, to unmanaged resourcesresources
GC alters it’s strategy, to increase the GC alters it’s strategy, to increase the number of collections performednumber of collections performed
GC.RemoveMemoryPressure when your GC.RemoveMemoryPressure when your object is freed, to allow the GC to return object is freed, to allow the GC to return to its standard strategyto its standard strategy
Resource Management Resource Management 2.0 Feature: 2.0 Feature:
MemoryPressureMemoryPressureclass Bitmap {
private long _size;Bitmap (string path ) { _size = new FileInfo(path).Length; GC.AddMemoryPressure(_size); // other work}~Bitmap() { GC.RemoveMemoryPressure(_size);
// other work}
}
class Bitmap {private long _size;Bitmap (string path ) { _size = new FileInfo(path).Length; GC.AddMemoryPressure(_size); // other work}~Bitmap() { GC.RemoveMemoryPressure(_size);
// other work}
}
Resource Management Resource Management 2.0 Feature: 2.0 Feature:
HandleCollectorHandleCollector HandleCollector keeps track of a limited number of HandleCollector keeps track of a limited number of
handles handles typically, unmanaged resource handles: HDCs, HWnds, etctypically, unmanaged resource handles: HDCs, HWnds, etc
When you allocate a new handle, call Add. When you allocate a new handle, call Add. When you freeing, call RemoveWhen you freeing, call Remove As you add to the collector, it may perform a As you add to the collector, it may perform a
GC.Collect(), to free existing handles, based on the GC.Collect(), to free existing handles, based on the current count, and the number of resources availablecurrent count, and the number of resources available
namename: allows you to track each handle type separately, if needed: allows you to track each handle type separately, if neededinitialThresholdinitialThreshold: the point at which collections should begin being : the point at which collections should begin being
performedperformedmaximumThresholdmaximumThreshold: the point at which collections MUST be : the point at which collections MUST be
performed. This should be set to the maximum number of available performed. This should be set to the maximum number of available handleshandles
HandleCollector(string name, int initialThreshold, int maximumThreshold);
HandleCollector(string name, int initialThreshold, int maximumThreshold);
Resource Management Resource Management 2.0 Feature: 2.0 Feature:
HandleCollectorHandleCollectorstatic readonly HandleCollector GdiHandleType =
new HandleCollector( “GdiHandles”, 10, 50);
static IntPtr CreateSolidBrush() {IntPtr temp = CreateSolidBrushImpl(…);GdiHandleType.Add();return temp;
}
internal static void DeleteObject(IntPtr handle) {DeleteObjectImpl(handle);GdiHandleType.Remove();
}
static readonly HandleCollector GdiHandleType = new HandleCollector( “GdiHandles”, 10, 50);
static IntPtr CreateSolidBrush() {IntPtr temp = CreateSolidBrushImpl(…);GdiHandleType.Add();return temp;
}
internal static void DeleteObject(IntPtr handle) {DeleteObjectImpl(handle);GdiHandleType.Remove();
}
More InformationMore Information
My Blog: My Blog: http://blogs.msdn.com/bradahttp://blogs.msdn.com/brada
The SLAR
Designing .NET Class Libraries: http://msdn.microsoft.com/netframework/programming/classlibraries/
FxCop is your ally in the fight: http://www.gotdotnet.com/team/fxcop/ .NET Framework Resource Management whitepaperResource Management with the CLR: The IDisposable Pattern
Special thanks to Brian Harry for significant input in the exceptions section
Applied Microsoft .NET Framework Programming
Back upBack up
Intro to Exception Handling Intro to Exception Handling in VBin VB
VB .NET has structured Exception HandlingVB .NET has structured Exception Handling
TryTry
Obj.DoSomeWorkObj.DoSomeWork
Catch e As ExceptionCatch e As Exception
LogError(e)LogError(e)
FinallyFinally
Obj.DisposeObj.Dispose
End TryEnd Try Can “Throw” an exceptionCan “Throw” an exception
Throw New System.Exception(“Error Message”)Throw New System.Exception(“Error Message”) Enhanced Exception objectEnhanced Exception object
Tracks nested exceptionsTracks nested exceptions Provides StackTrace to help pinpoint errorProvides StackTrace to help pinpoint error
Try '<code that may fail> Catch ex As Exception '<error handling code>
Finally '<clean up>End Try
fReRaise = False
OnError GoTo ErrHandler '<code that may fail> GoTo CleanUp
ErrHandler: If '<condition we can handle> Then '<error handling code> Else fReRaise = True End If
CleanUp: If fReRaise Then Err.Raise errNum
On Error vs. On Error vs. Try/Catch/FinallyTry/Catch/Finally
OnError GoTo ErrHandler '<code that may fail>
ErrHandler: If '<condition we can handle> Then '<error handling code>
fReRaise = FalseTry '<code that may fail>
Else fReRaise = True End IfCleanUp: If fReRaise Then Err.Raise errNum
Catch ex As Exception '<error handling code>Finally '<clean up>
VB 2005 Exception VB 2005 Exception SupportSupport Exception AssistantException Assistant
A dialog pops up when an exception occurs in your application that A dialog pops up when an exception occurs in your application that describes the exception type and the common ways reasons for it describes the exception type and the common ways reasons for it being thrown. You can set any exception type you like to be a “first being thrown. You can set any exception type you like to be a “first chance exception” which would bring up the dialog in the first chance exception” which would bring up the dialog in the first place it encountered this exception via the DebugàExceptions place it encountered this exception via the DebugàExceptions menus. menus.
Overlapping Exception Warnings Overlapping Exception Warnings VB warns when you’ve caught a supertype exception before its VB warns when you’ve caught a supertype exception before its
subtype (thus indicating dead code.)subtype (thus indicating dead code.) IDispose spitIDispose spit
Visual Basic will now spit the IDisposable pattern when you type Visual Basic will now spit the IDisposable pattern when you type Implements IDispose and commit. Implements IDispose and commit.
Unhandled Exception eventUnhandled Exception event VB now has application events which you can access by opening VB now has application events which you can access by opening
the app designer, selecting the “view application events” button, the app designer, selecting the “view application events” button, and using the drop downs to navigate to the UnhandledException and using the drop downs to navigate to the UnhandledException event. event.
Using statement Using statement In 2005, VB will support it too. In 2005, VB will support it too.
Application tracing Application tracing VB now supports an extremely easy way to do application logging. VB now supports an extremely easy way to do application logging.
Just type My.Application.Log.WriteException() and go from there…Just type My.Application.Log.WriteException() and go from there…
Using Exceptions: Using Exceptions: Creating new Exceptions Creating new Exceptions
(continued)(continued) Every exception should have at Every exception should have at least the top three constructorsleast the top three constructors
public class XxxException : YyyException { public XxxException () {} public XxxException (string message) {} public XxxException (string message,
Exception inner) {} protected XxxException (
SerializationInfo info,StreamingContext context) {}
}
public class XxxException : YyyException { public XxxException () {} public XxxException (string message) {} public XxxException (string message,
Exception inner) {} protected XxxException (
SerializationInfo info,StreamingContext context) {}
}
Note: making an exception fully Note: making an exception fully serializable implies a little more work…serializable implies a little more work…
Using Exceptions: Bad Using Exceptions: Bad PracticePractice
ArgumentNullException does not follow ArgumentNullException does not follow this patternthis pattern
Justification: Argument names are much Justification: Argument names are much more common than messagemore common than message
public class ArgumentNullException : ArgumentException {
public ArgumentNullException () {} public ArgumentNullException (string paramName) {} public ArgumentNullException (string paramName,
string message) {}}
public class ArgumentNullException : ArgumentException {
public ArgumentNullException () {} public ArgumentNullException (string paramName) {} public ArgumentNullException (string paramName,
string message) {}}
Using Exceptions: Bad Using Exceptions: Bad PracticePractice
Result: Habit wins out and people Result: Habit wins out and people commonly type:commonly type:throw new ArgumentNullException ("the value must
pass an employee name");
throw new ArgumentNullException ("the value must pass an employee name");
throw new ArgumentNullException ("Name", "the value must pass an employee name");
throw new ArgumentNullException ("Name", "the value must pass an employee name");
Unhandled Exception: System.ArgumentNullException: Value cannot be null.Parameter name: the value must pass an employee name
Unhandled Exception: System.ArgumentNullException: Value cannot be null.Parameter name: the value must pass an employee name
• Rather than:
• We end up with odd error messages such as:
• Lesson: Just follow the pattern!
Recommended