Beruflich Dokumente
Kultur Dokumente
brada@microsoft.com http://blogs.msdn.com/brada
Agenda
Exception
“Cleaner, more elegant, and wrong.”
Raymond Chen (http://blogs.msdn.com/oldnewthing/)
Memory Management
“I'm addicted to deterministic destruction”
Chris Sells (http://www.sellsbrothers.com/)
Exception Handling
Questions
Throwing an exception
What exception to throw?
What data to include in the exception?
Managing resources
What resources need to be cleaned up?
When should they be cleaned up?
Catching an Exception
When to catch an exception?
How to report the error?
What to do with the control follow: terminate
the app, abort the transaction, ignore the
exception, disable some functionality, etc.?
When to Throw?
Exceptions rather than error codes
Robust: failures get noticed
Your method is defined to do
something…
If it succeeds in performing its purpose,
return
If it fails to do what it was written to do,
throw an exception
What to throw?
Use or subclass existing exceptions
if at all possible
Only create separate classes if you
think developers will handle the
exception differently
try
try {{
//some
//some operation
operation
}}
catch
catch (FileNotFoundException
(FileNotFoundException fe) fe) {{
//do
//do some
some set
set of
of work
work
}}
catch
catch (DriveNotFoundException
(DriveNotFoundException be) be) {{
//do
//do some
some other
other set
set of
of work
work
}}
Throwing an Exception
Do not just map error codes on to a
single exception with an error code
property (e.g., the WMIException)
Use separate exception types
Error Messages
Consider localization
Use a complete sentence (end in a
period)
Don’t expose privacy related information
(such as file paths)
Performance
Minimize the number of exceptions you
throw in your API’s success code-paths
You don’t pay for exceptions until you throw in
managed code
Throwing exceptions degrades performance
Perf counters tell you exactly how many
exceptions your application is throwing
Only an issue when using exceptions as control
flow
Consider providing a way to avoid an
exception being thrown
Performance (continued)
int
int i;i;
try
try {{
ii == Int32.Parse(“123”);
Int32.Parse(“123”);
}}
catch
catch (FormatException
(FormatException )) {{
Console.WriteLine
Console.WriteLine (“Invalid”);
(“Invalid”);
}}
int
int i;
i;
if
if (!Int32.TryParse
(!Int32.TryParse (“123”,
(“123”, out
out i))
i))
{{
Console.Writeline(“Invalid”);
Console.Writeline(“Invalid”);
}}
Managing Resources
You should use try..finally 10 times
as often as try..catch
Catches eat exceptions making it hard
to debug
Finally allows you to clean up, but let
the exception continue
Managing Resources
try
try {{
.. .. ..
}}
catch
catch (DivisionByZeroException
(DivisionByZeroException e) e) {{
//
// do
do clean
clean up
up work
work
throw
throw new
new BetterException
BetterException (message,
(message, e);
e);
}}
Memory Management
“I'm addicted to deterministic destruction”
Chris Sells (http://www.sellsbrothers.com/)
Memory Management
The GC does an excellent job managing
“managed” memory
GC doesn’t manage external resources (DB
connections, HWnds, etc.)
Generational Mark-and-sweep garbage
collection means non-deterministic finalization
Exact time of finalization is unspecified
Order of finalization is unspecified
Thread is unspecified
Resource Management
If you are encapsulating external
resources:
Add a finalizer (C# destructor) to guarantee
the resource will eventually be freed
Provide developers an explicit way to free
external resources
Formalized in the IDisposable interface
Signals to users they need to explicitly Dispose of
instances
Enables C# and VB (2005) using support
Finalizers
Object.Finalize() is not accessible
in C#
VERY
public
public class different than C++’s
class Resource
Resource
{{
destructors
~Resource()
~Resource() {{ public
public class
class Resource
Resource
...
... {{
}} protected
protected override
override void
void Finalize()
Finalize() {{
}} try
try {{
...
...
}}
finally
finally {{
base.Finalize();
base.Finalize();
}}
}}
}}
Finalizers (2)
Only implement Finalize on objects that
need finalization
Finalization is only appropriate for cleanup of
unmanaged resources
Keeps objects alive an order of magnitude
longer
Free any external resources you own in
your Finalize method
Do not throw exceptions in finalizers
The rest of your finalizer will not run
Check out Critical Finalizers in 2.0
Finalizers (3)
Do not block or wait in finalizers
All finalization for that process could be
stopped
Only release resources that are held
onto by this instance
Do not reference other instances
Will be called on one or more
different threads
Dispose Pattern
Implement the dispose pattern
whenever you have a finalizer
Gives developers explicit control
Free any disposable resources your
type owns in the Dispose() method
Not just the external resources
Propagate calls to Dispose() through
containment hierarchies
Dispose Pattern (2)
Suppress finalization once Dispose() has
been called
Dispose() should be callable multiple
times without throwing an exception
The method will do nothing after the first call
After Dispose() is called other methods on
the class can throw ObjectDisposedException
Do not assume that Dispose() will be called
For unmanaged cleanup have a finalizer as well
Do call your base class’s Dispose(bool)
method if it implements IDisposable
Implementing
public class IDisposable
public class Resource: IDisposable
Resource: IDisposable {{
private
private bool
bool disposed
disposed == false;
false;
pubic
pubic int
int GetValue
GetValue ()
() {{
if
if (disposed)
(disposed) throw
throw new
new ObjectDisposedException();
ObjectDisposedException();
//
// do
do work
work
}}
public
public void
void Dispose()
Dispose() {{
if
if (disposed)
(disposed) return;
return;
Dispose(true);
Dispose(true);
GC.SuppressFinalize(this);
GC.SuppressFinalize(this);
}}
protected
protected virtual
virtual void
void Dispose(bool
Dispose(bool disposing)
disposing) {{
if
if (disposing)
(disposing) {{
//
// Dispose
Dispose dependent
dependent objects
objects
disposed
disposed == true;
true;
}}
//
// Free
Free unmanaged
unmanaged resources
resources
}}
~Resource()
~Resource() {{
Dispose(false);
Dispose(false);
}}
}
Using Statement
Acquire, Execute, Release pattern
Works with any IDisposable object
Data access classes, streams, text
readers and writers, network classes,
using etc.
using (Resource
(Resource res
res == new
new Resource())
Resource()) {{
res.DoWork();
res.DoWork();
}} Resource
Resource res
res == new
new Resource(...);
Resource(...);
try
try {{
res.DoWork();
res.DoWork();
}}
finally
finally {{
if
if (res
(res !=
!= null)
null)
((IDisposable)res).Dispose();
((IDisposable)res).Dispose();
}}
Using Statement
Acquire, Execute, Release pattern
Works with any IDisposable object
Data access classes, streams, text
readers and writers, network classes,
Using etc.As Resource
Using res
res As Resource == New
New Resource
Resource ()
()
res.DoWork()
res.DoWork()
End Dim
Dim res
res As
As New
New Resource()
End Using
Using Resource()
Try
Try
res.DoWork()
res.DoWork()
Finally
Finally
If
If res
res IsNot
IsNot Nothing
Nothing Then
Then
CType(res,
CType(res, IDisposable).Dispose()
IDisposable).Dispose()
End
End If
If
End
End Try
Try
Using Statement (2)
Can you find the “bug” in this code?
Will input and output always be
closed?
static
static void
void Copy(string
Copy(string sourceName,
sourceName, string
string destName)
destName) {{
Stream
Stream input
input == File.OpenRead(sourceName);
File.OpenRead(sourceName);
Stream
Stream output
output == File.Create(destName);
File.Create(destName);
byte[]
byte[] bb == new
new byte[65536];
byte[65536];
int
int n;
n;
while
while ((n
((n == input.Read(b,
input.Read(b, 0,
0, b.Length))
b.Length)) !=
!= 0)
0) {{
output.Write(b,
output.Write(b, 0, 0, n);
n);
}}
output.Close();
output.Close();
input.Close();
input.Close();
}}
Using Statement (3)
static
static void
void Copy(string
Copy(string sourceName,
sourceName, string
string destName)
destName) {{
Stream
Stream input
input == File.OpenRead(sourceName);
File.OpenRead(sourceName);
Stream
Stream output
output == File.Create(destName);
File.Create(destName);
try
try {{
byte[]
byte[] bb == new
new byte[65536];
byte[65536];
int
int n;
n;
while
while ((n
((n == input.Read(b,
input.Read(b, 0,0, b.Length))
b.Length)) !=
!= 0)
0) {{
output.Write(b,
output.Write(b, 0, 0, n);
n);
}}
}}
finally
finally {{
output.Close();
output.Close();
input.Close();
input.Close();
}}
}}
Using Statement (4)
static
static void
void Copy(string
Copy(string sourceName,
sourceName, string
string destName)
destName) {{
Stream
Stream input
input == File.OpenRead(sourceName);
File.OpenRead(sourceName);
try
try {{
Stream
Stream output
output == File.Create(destName);
File.Create(destName);
try
try {{
byte[]
byte[] bb == new
new byte[65536];
byte[65536];
int
int n;
n;
while
while ((n
((n == input.Read(b,
input.Read(b, 0,0, b.Length))
b.Length)) !=
!= 0)
0) {{
output.Write(b,
output.Write(b, 0, 0, n);
n);
}}
}}
finally
finally {{
output.Close();
output.Close();
}}
}}
finally
finally {{
input.Close();
input.Close();
}}
}}
Using Statement (4)
Code is correct and much more
readable with using statements
Types should implement IDisposable to
take advantage of this support
static
static void
void Copy(string
Copy(string sourceName,
sourceName, string
string destName)
destName) {{
using
using (Stream
(Stream input
input == File.OpenRead(sourceName))
File.OpenRead(sourceName))
using
using (Stream
(Stream output
output == File.Create(destName))
File.Create(destName)) {{
byte[]
byte[] bb == new
new byte[65536];
byte[65536];
int
int n;
n;
while
while ((n
((n == input.Read(b,
input.Read(b, 0,0, b.Length))
b.Length)) !=
!= 0)
0) {{
output.Write(b,
output.Write(b, 0, 0, n);
n);
}}
}}
}}
Resource Management
2.0 Feature:
MemoryPressure
GC.AddMemoryPressure ( int pressure
)
Useful when you have a disproportionate
ratio of managed, to unmanaged
resources
GC alters it’s strategy, to increase the
number of collections performed
GC.RemoveMemoryPressure when your
object is freed, to allow the GC to return
to its standard strategy
Resource Management
2.0 Feature:
class
MemoryPressure
Bitmap {
class Bitmap {
private
private long
long _size;
_size;
Bitmap
Bitmap (string
(string path
path )) {{
_size
_size == new
new FileInfo(path).Length;
FileInfo(path).Length;
GC.AddMemoryPressure(_size);
GC.AddMemoryPressure(_size);
//
// other
other work
work
}}
~Bitmap()
~Bitmap() {{
GC.RemoveMemoryPressure(_size);
GC.RemoveMemoryPressure(_size);
//
// other
other work
work
}}
}}
Resource Management
2.0 Feature:
HandleCollector
HandleCollector keeps track of a limited number of handles
typically, unmanaged resource handles: HDCs, HWnds, etc
When you allocate a new handle, call Add.
When you freeing, call Remove
As you add to the collector, it may perform a GC.Collect(), to
free existing handles, based on the current count, and the
number of resources available
HandleCollector(string
HandleCollector(string name,
name, int
int initialThreshold,
initialThreshold,
int
int maximumThreshold);
maximumThreshold);
name: allows you to track each handle type separately, if needed
initialThreshold: the point at which collections should begin being performed
maximumThreshold: the point at which collections MUST be performed. This
should be set to the maximum number of available handles
Resource Management
2.0 Feature:
HandleCollector
static readonly HandleCollector GdiHandleType =
static readonly HandleCollector GdiHandleType =
new
new HandleCollector(
HandleCollector( “GdiHandles”,
“GdiHandles”, 10,
10, 50);
50);
static
static IntPtr
IntPtr CreateSolidBrush()
CreateSolidBrush() {{
IntPtr
IntPtr temp
temp == CreateSolidBrushImpl(…);
CreateSolidBrushImpl(…);
GdiHandleType.Add();
GdiHandleType.Add();
return
return temp;
temp;
}}
internal
internal static
static void
void DeleteObject(IntPtr
DeleteObject(IntPtr handle)
handle) {{
DeleteObjectImpl(handle);
DeleteObjectImpl(handle);
GdiHandleType.Remove();
GdiHandleType.Remove();
}}
More Information
My Blog:
http://blogs.msdn.com/brada
• Rather than:
throw
throw new
new ArgumentNullException
ArgumentNullException ("Name",
("Name", "the
"the value
value
must
must pass
pass an
an employee
employee name");
name");