Title: The Art of Building a Reusable Class Library
1The Art of Building a Reusable Class Library
- Brad Abrams BradA_at_Microsoft.com Krzysztof
Cwalina - KCWalina_at_Microsoft.com - Microsoft Corporation
2Turn frustrated developers
into happy developers
3Goals
- Understand that API design matters
- Recognize good API design
- Lots of good and bad examples
- Learn the API design process
4Who is this precon for?
- 1. Developers building reusable library or
component
- 2. Developer building apps to explicitly
understand the rules of the .NET Framework and
WinFX
5Quote of the day
- I have yet to see any problem, however
complicated, which, when looked at the right way
did not become still more complicated. - Poul Anderson
6Project Cost Model
Source Gartner, May 2002 The Cost and Risk of
Application Development DecisionsJoseph Feiman,
Tom Berg
7Four Keys of Framework Design
8The Power of Sameness
Brad Abrams
9When you pick up your rental car.
- Push the seat all the way back
- Find an NPR station
- Find the exit
10Oh, down to lock
11How to use a key
12Oh, you push the PRESS button
13Who actually needs this data?
14Why you dont read rental car manuals
- You know how to drive your car
- All cars work basically the same way
- Your rental car is a car
- Therefore, you can drive your rental car
- That is
The Power of Sameness
15Producers and Sameness
New System
Market Potential
Standard System
16Are things equally as obvious in your framework
designs? Maybe you are missing.
The Power of Sameness
17Naming Conventions
- PascalCasing Each word starts with an uppercase
letter
- camelCasing First word lower case, others
uppercase
- SCREAMING_CAPS All upper case with underscores
18Blink Quiz I
class1 MyClass
Blink The Power of Thinking Without Thinking
19Blink Quiz I
- At-a-glance, first impressions what does this
likely do? - A Compiler error
- B Class declaration
- C Variable declaration
- D Other
class1 MyClass
- Change the casing to match the pattern, is it
easier to tell what this is?
Class1 myClass
20Naming Conventions
- All types and publicly exposed members are
PascalCased - Parameters are camelCased
public class MemberDoc public int
CompareTo(object value) public string Name
get
Section 4.1, Naming Conventions
21Hungarian Notation
- Do not use Hungarian notation in publicly exposed
APIs and parameter names
public class CMyClass int CompareTo (object
objValue) .. string lpstrName get int
iValue get
22Hungarian Notation (continued)
- The prefix codes are arbitrary and difficult to
learn - They make it harder to read an identifier name
- Hungarian was developed for a time when languages
were loosely typed and IDEs were not as powerful - E.g. out and ref parameters information now
conveyed at the call site. - Interlocked.Increment(ref i)
23On Abbreviations, acronym, initialism and the
like
- Avoid them!
- They are a classic JLT (jargon loaded term)
- OK to use them once they become words
- Html, Xaml, etc
- Dont just spell them out
- Use a meaningful name
- Abbreviations of more than 2 letters are cased as
words, otherwise ALLUPPER - IO vs. Html
24While we are on naming
- Good naming is hardit takes time
- Be meaningful but brief
- Use US-English
- Colour vs. Color
- Principle of least surprise
- Look for prior-art
- NumberOfElements vs. Count
25Blink Quiz II
IFoo foo new IFoo()
- No because you cant create instances of
interfaces - Why do we assume IFoo is an interface?
The Power of Sameness
26Suffixes and Prefixes
- Interfaces prefix with I
- A tribute to COM
- Exceptions and Attributes suffixed
public interface IFormattable Public class
NameAttribute Attribute Public class
PrinterOnFireException Exception
27Quick Quiz
- What is wrong this this type?
public class ArgumentNullException
ArgumentException public
ArgumentNullException () public
ArgumentNullException (string paramName)
public ArgumentNullException (string paramName,
string message)
28Common Exception Usage
throw new IOException()
throw new FormatException()
throw new ArgumentException()
throw new InvalidOperationException()
- Developers expect all exceptions to work this
way - Make your exception meet expectations
29Exception Constructor Pattern
- Every exception should have at least 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)
30Remember this type.
- What is wrong this this type?
public class ArgumentNullException
ArgumentException public
ArgumentNullException () public
ArgumentNullException (string paramName)
public ArgumentNullException (string paramName,
string message)
31API design theater
You are optimizing locally, you are making the
right decision if developers only use your type,
but if they use others as well.
I see why not?
90 of the time the message is not used, the
param name is what is important
It doesnt apply in this case
Why didnt you follow the exception pattern?
The Developer
The Architect
32Missing the power of sameness
- Result Habit wins out and people commonly type
throw new ArgumentNullException ("the value must
pass an employee name")
- We end up with odd error messages such as
Unhandled Exception System.ArgumentNullException
Value cannot be null.Parameter name the value
must pass an employee name
- Moral Just follow the pattern!
throw new ArgumentNullException ("Name", "the
value must pass an employee name")
33Method OverloadingAbusing the Power of Sameness
- Overloaded Method on the same type with the same
name, but different number or type of arguments
public static string ToString(int value)
public static string ToString(double value)
34Dangers of Method Overloading
- Why does this man dislike method overloading?
- Meyer A single operation should do a single
thing..
- Abrams Good method overloading is a single thing
all overloads are semantically the same
35Method Overloading
String.IndexOf(string value) String.IndexOf(cha
r anyOf)
string s "brad abrams"s.IndexOf("ab") //
5s.IndexOf('ab') // 0
- Cobol actually does allow syntax like this
- Moral Dont overload when the methods are
semantically different
36Method Overloading
- Use overloading only when the overloads do
semantically the same thing - Incorrect overload
-
- Correct overload
String.IndexOf(string value) String.IndexOf(cha
r anyOf)
Convert.ToString(int value) Convert.ToString(do
uble value)
37Method Overloading Defaults
- Use appropriate default values
- Simple method assumes default state
- More complex methods indicate changes from the
default state -
-
- Use a zeroed state for the default value (such
as 0, 0.0, false, null, etc.)
MethodInfo Type.GetMethod (string name)
//ignoreCase false MethodInfo Type.GetMethod
(string name, boolean ignoreCase)
38Constructors and Properties
- Constructors parameters are shortcuts for
setting properties - No difference in semantics between these code
snippets
EventLog log new EventLog()log.MachineName
kcwalina0log.Log Security
EventLog log new EventLog(Security)log.Machi
neName kcwalina0
EventLog log new EventLog(Security,kcwalina0
)
39Exercises
40Exercise Code Review
public class HtmlEncoding public const
string DEFAULT_NAME "Html3.2" public
HtmlEncoding (int iCount) .. public string
Tagname get .. public bool
UseIoCompletionPort get .. protected void
_coreSetValue (double value) .. private
IntPtr ptrValue
41Exercise Naming Conventions
- What are good reasons to violate naming
conventions? - I used to work in C, Java, COBOL
- Our previous version used SCREAMING_CAP
- These are initials from someones name, they
have to be all upper case
42Exercise Code Review
public interface Convertible Public class
CodeReviewer Attribute Public class
InputInvalidError Exception
43Exercise Code Review
public static int MaxValue 9999999 public void
Withdraw () Withdraw (MaxValue) public
void Withdraw (int amount)
44Summary
- The Power of Sameness Teaches us
- Influence of expectations
- Naming conventions and common suffixes\prefixes
- Habits win out over the special cases
- Common Exception pattern
- With great power comes great responsibility
- Method overloading
- Meet developers where they are
- constructors and properties pattern teaches us to
meet developers expectations
45Questions?
46Framework Design Matters
- The role of a framework designer in the
development process - Krzysztof Cwalina
- Microsoft
47The process of framework design is very different
from prototyping and implementation
48Developers focus on a different level
49Similarly to implementations framework design
must be
- Simple
- Integrated
- Consistent
- Evolvable
... but the similarities are superficial
50Well-designed framework must be simple
51Focus on Top Scenarios
- Do define top usage scenarios for each major
feature area. - Do design APIs by first writing code samples for
the main scenarios and then defining the object
model to support the code samples. - Do not require users to explicitly instantiate
more than one type in the most basic scenarios.
52Example Scenario Samples
53 Namespace Factoring
- Avoid having types designed for advanced
scenarios in the same namespace as types intended
for common programming tasks. - Do ensure that each main feature area namespace
contains only types that are used in the most
common scenarios. Types used in advanced
scenarios should be placed in subnamespaces.
54Example Namespace Factoring
55Naming
- Do favor readability over brevity. The property
name CanScrollHorizontally is better than
ScrollableX (an obscure reference to the X-axis). - Do not use abbreviations or contractions as parts
of identifier names. - Do not use any acronyms that are not widely
accepted, and then, only when necessary. - Do reserve the best type names for the most
commonly used types.
56Example Naming
57Exceptions
- Do use exception messages to communicate
framework usage mistakes to the developer.
58Example Exceptions
EventLog log new EventLog() Log.WriteEntry(Hel
lo World)
59Well-designed framework must be explicitly
designed
60Create API Specification
61Review Scenario Samples
- Solicit feedback on the scenario samples
- Feedback from non-experts
62Review API Design
63Well-designed framework is a part of an ecosystem
64Intellisense
65EditorBrowsableAttribute
public class MyComponent EditorBrowsable(Ed
itorBrowsableState.Always) public void
CommonlyUsedMethod()
EditorBrowsable(EditorBrowsableState.Never)
public void RarelyUsedMethod()
EditorBrowsable(EditorBrowsableState.Never
) public override int GetHashCode()
return base.GetHashCode()
66Naming Related Members
67Naming Related Members Fixed
68Debugger
69Debugger Attributes
DebuggerTypeProxy(typeof(LinkedListDebuggerViewltgt
)) public class LinkedListltTgt internal
class LinkedListDebuggerViewltTgt public
LinkedListDebuggerView(LinkedListltTgt list)
DebuggerBrowsable(DebuggerBrowsableState.R
ootHidden) public T Items get
70Debugger Attributes Result
71CLS Compliance
- Do apply CLSCompliantAttribute to framework
assemblies.
assemblyCLSCompliant(true)
72Example CLS Compliance
73Well-designed framework must be integrated
74Using Common Abstractions
- Do use the least derived parameter type that
provides the functionality required by the
member. - Do use the least specialized type possible as a
parameter type. Most members taking collections
as parameters use IEnumerableltTgt interface. - Do implement IEquatableltTgt on value types.
75Collection Abstractions
76Collection Abstractions Fixed
77Type Name Conflicts
- Do not introduce generic type names such as
Element, Node, Log, and Message.
78Example Type Name Conflicts
79Type Name Conflicts Fixed by User
80Type Name Conflicts Fixed by Framework Designer
81Well-designed framework must be designed to
evolve
82Interfaces vs. Abstract Classes
- Do favor defining classes over interfaces.
83Example Adding Members to Abstractions
public abstract class Stream // new Stream
members to support timeouts public virtual
int ReadTimeout get throw new
NotSupportedException() set throw
new NotSupportedException()
public virtual bool CanTimeout get
return false // other Stream members
that shipped previously
84Example Adding Functionality in Subclasses
public class FileStream Stream public
override bool CanTimeout get return
true public override int
ReadTimeout get set
85Example Interface Based Design
public interface IStream public class
FileStream IStream
86Example Interface Based Design Adding Timeout
public interface ITimeoutEnabledStream IStream
int ReadTimeout get set public
class FileStream ITimeoutEnabledStream
public int ReadTimeout get
set
87Example Interface Based Design Problem
StreamReader reader GetSomeReader() //
dynamic cast (BaseStream returns
Stream) ITimeoutEnabledStream stream
reader.BaseStream as ITimeoutEnabledStream if(st
ream ! null) stream.ReadTimout 100
88Example Abstract Class is Better
StreamReader reader GetSomeReader() if(reader.B
aseStream.CanTimeout) reader.BaseStream.ReadT
imeout 100
89Control Extensibility
- Do not make members virtual unless you have a
good reason to do so and you are aware of all the
costs related to designing, testing, and
maintaining virtual members.
90Test Abstractions
- Do provide at least one type that is an
implementation of an interface. - Do provide at least one API consuming each
interface you define (a method taking the
interface as a parameter or a property typed as
the interface).
91Well-designed framework must be consistent
92Naming Consistency
93Common Patterns and Idioms
- Attribute Design
- Collection Usage
- XML Usage
- The Async Pattern
- Dispose Pattern
94Exercise API Design Process
- Which of the following is the best way to start
API design? - Sit in front of a computer and start coding.
- Use class diagrams to discover main entities of
the system. - Create a list of most common scenarios and write
code samples corresponding to the scenarios.
95Exercise Ecosystem
- What parts of the development environment need to
be considered when designing APIs? - Intellisense
- Debuggers
- Documentation
- All of the above
96Exercise Simplicity and Usability
- What is the main contributor to framework
usability? - How easy it is to find the right type to use.
- How easy it is to find the right member to use.
- Clear exception messages.
97Summary
- Framework design does not happen magically
- Best frameworks are designed upfront by framework
designers - There are several qualities of a well-designed
framework that require focused framework design
process
98Questions?
99API Design ExperienceThe API review
- Krzysztof Cwalina
- Brad Abrams
100Goals for this Exercise
- Demonstrate how the API design experience really
goes - Show you some common mistakes and common fixes
- Laugh with us as we roll play this..
101Comment KC1Why use an interface rather than a
base class?
102Comment KC1Why is this sealed?
103Comment KC1Should it be a value type?
104Comment KC1Make this a static class?
105Comment KC2Why not throw an exception?
106(No Transcript)
107What we showed you
- Demonstrated a few examples of how the API design
experience really goes - Show you some common mistakes and common fixes
108Tools for Communication
Brad Abrams
109Focus on developer, not yourself
110Your developers cant read your mind
111You must communicate
- Examples of what consumers need to know
- Will this operation block?
- How do I customize this type?
- How should I use this class?
112Wouldnt it be great
Luckily, there are Tools for Communicating
113Documentation alone is not enough
114Good documentation on a bad API is
like lipstick on a pig
115Communicate via leaving artifacts
- Framework Design Artifacts
- Types
- Methods
- Properties
- Events
- Constructors
116Why is Archeology so hard?
- All we have is what they left us
- We cant directly talk to anyone that built them
- Hard to find pieces of fragments that fit
together - Bottom line ancient civilizations were not
intentional about what they left behind
Sound Familiar?
Rosetta Stone
117Follow a common vocabulary
Dont force consumers to become archeologists of
your framework
118Follow a Common Vocabulary
- Pre-School level vocabulary for English
- What is the common vocabulary for API design?
119Define Namespace
namespace System.IO
- Organizational principle to allow consumers to
- Find relevant functionality quickly
- Exclude less relevant functionality
- Not about implementation issues
- Security, identity, size, perf
- Not a billboard to advertise your organizational
namespace System.IO.Readers namespace
System.IO.Writters namespace System.IO.Streams
120Define Class
public class Object
- A conceptual model for a thing which can hold
state, perform actions, etc - Common API design problems
- Grab bag types (lack of cohesion)
- Example System.Runtime.InteropServices.Marshal
- Modeling overly abstract
- Example StreamReader vs. File
public static class Marshal
121Define Struct
public struct Int32 IComparable,
IFormattable
- A domain specific extension of the intrinsic type
system - Example Point, Complex, etc
- Expert use perf optimization when GC-Heap
allocated objects not warranted - Example an Enumerator
- Common API design problems
- Overuse to avoid GC
- instance size over 16 bytes
- Are not immutable
122Quick Quiz
- What is wrong with this type?
public sealed class Environment private
Environment () public bool
HasShutDownStarted get
123Define Static Class
- Container for a set of highly related static
members. Commonly used for - Full OO encapsulation not warranted
- Example System.Math
- Convenience methods for a more complex design
- Example System.IO.File
- Common API design problems
- Doing it by hand and getting it wrong
- Loose cohesion
124Static Classes Under the Covers
public static class Environment public static
string CommandLine get ..
public abstract sealed class Environment
public static void Exit(int exitCode) ..
public static string CommandLine get ..
125Define Exceptions
public class FileNotFoundException IOException
- Encapsulation of error details used in a
structured exception handling system - Common API Design Mistakes
- Using error codes rather the exceptions
- Creating far too many exceptions
- Only create new exceptions if callers will handle
them differently
126Define Enum
public enum Colors Red 0, Green
1, Blue 2
- Container for named constants
- Common API Design Mistakes
- Accepting the default values
- Using magic constants instead
SetColor(Colors.Red)SetColor(1)
127Enums Under the Covers
- Enum values default to 0..N
public enum Compression Zip, SuperZip,
None
public struct Compression Enum public int
value__ public const Compression Zip 0
public const Compression SuperZip 1 public
const Compression None 2
128Define Flags Enums
Flags public enum AttributeTargets
Assembly 0x0001, Module 0x0002,
Class 0x0004, Struct 0x0008, Type
Class Struct,
- Combinable enum
- FlagsAttibute
- powers of 2
- Use plural names
- AttributeTargets rather than AttributeTarget
- Provide named constants for common flags
129Flag Enums Common Mistakes
- Only use Flag enums when they represent a single
concept - AttributeTargets is good example
- The Reflection BindingFlags is an example of what
not to do - Contains many different concepts in a single
value (visibility, static-ness, member kind,
etc.) - Should not be suffixed in Flags
130Define Constructor
public class Employee public Employee (string
name)
- Facility to capture state for an instance at time
of instantiation - Common API design problem
- Doing too much work in the constructor
131Constructors are
public class XmlFile string fileName Stream
data public XmlFile(string fileName)
this.data DownloadData(fileName)
- Do minimal work in the constructor
- Be Lazy!
- Only capture the parameters
132Define Methods
public sealed class String public string
Insert (string value, int position)
- Used to expose actions or operations
- Common API Design Problem
- Using properties where methods should be used
133Properties versus Methods
- Use a Property
- If the member logical attribute of the type
- Use a method
- If the operation is a conversion, such as
ToString() - If the getter has an observable side effect
- If order of execution is important
- If the method might not return immediately
- If the member returns an array
134Properties and returning arrays
public Employee All get
public Employee GetAll()
Calling Code
creates 2n1 copies of the arrays
EmployeeList l FillList() for (int i 0 i lt
l.Length i) if (l.Alli x)...
if (l.GetAll()i x) ...
Moral Use method if the operation is expensive
135Define Fields
- Useful for exposing implementation details
thereby constraining your ability to evolve the
framework - Or, just use properties ?
public String FileName
private String fileNamepublic String FileName
get return fileName set fileName
value
136Const versus Readonly
- const
- Compile-time evaluation
- Stable across versions
- readonly
- Run-time evaluation
- Unstable across versions
class Math public const double Pi
3.14 class Color public static readonly
Color Red new Color(...) public static
readonly Color Blue new Color(...) public
static readonly Color Green new Color(...)
137Define Properties
public class ArrayList public int Count
get
- Logical backing field. Useful to encapsulate
access to state allowing a degree of flexibility
in implementation - Common API Design Problem
- Property vs. Method confusion
138Properties
- Use read only properties where appropriate
- Do not use write-only properties
- Property getters should be simple and therefore
unlikely to throw exceptions - Properties should not have dependencies on each
other - Setting one property should not affect other
properties - Properties should be settable in any order
139Define Events
public delegate void EventHandler(object sender,
EventArgs e) public class Button Control
public event EventHandler Click protected
void OnClick(EventArgs e) if (Click !
null) Click(this, e)
Event Handler
Event
Raise Method
- Expose callback operation
140Common Design Problem with Events
- Using bad terminology
- Events are not fired or triggered, they are
raised - Not using verbs
- E.g. Click, Paint, DrawItem, DropDown
- Not using strongly typed EventArgs to allow for
extensibility
public class MouseEventArgs
EventArgs
141Static Members
- Statics are the .NET equivalent of global
variables or global functions - Not object oriented
- Same evils as global
- But can be very useful
- System.Math Full modeling not required
public class Int32 public static int Parse
(string value) ..
int i Int32.Parse ("42")
142Exercises
143Exercise Namespaces
- What is a good reason to split some related
functionality across two namespaces? - Two different orgs create them
- They are in two different assemblies
- One is the 90 usage case the other in the 10
usage case
144Exercise class or struct?
public ??? DataValues decimal d1
decimal d2 decimal d3 decimal d4
public void SetValue (decimal d)
145Exercise Exceptions
- What are good reasons to create a new exception
type? - I get paid by number of types
- The underlying system call returns two different
values - Users handle the two cases differently
146Exercise Enums
public enum Fruits Banana, Apple,
Peach, Blueberry
147Summary
- Dont force your consumers to be archeologists
digging for how to use your framework - Every element in your design has a specific
meaning Know them and use them correctly - Avoid the common mistakes
- Make brand new mistakes
148Questions
149API Design ExperienceDesigning From Scratch
- Krzysztof Cwalina
- Brad Abrams
150Goals for this Exercise
- Demonstrate how the API design experience really
goes - Show you some common mistakes and common fixes
- How to identify a complex design
- How to use scenarios to fix the design
- Laugh at with us as we roll play this..
151Assignment Design a Serial Port API
152(No Transcript)
153 if(value ! handshake) // in the DCB,
handshake affects the fRtsControl, fOutxCtsFlow,
and fInX, fOutX fields, // so we must save
everything in that closure before making any
changes. Handshake handshakeOld handshake int
fInOutXOld GetDcbFlag(NativeMethods.FINX) int
fOutxCtsFlowOld GetDcbFlag(NativeMethods.FOUTXCT
SFLOW) int fRtsControlOld GetDcbFlag(NativeMeth
ods.FRTSCONTROL) handshake value int
fInXOutXFlag (handshake Handshake.XOnXOff
handshake Handshake.RequestToSendXOnXOff) ? 1
0 SetDcbFlag(NativeMethods.FINX,
fInXOutXFlag) SetDcbFlag(NativeMethods.FOUTX,
fInXOutXFlag) SetDcbFlag(NativeMethods.FOUTXCTSFL
OW, (handshake Handshake.RequestToSend
handshake Handshake.RequestToSendXOnXOff) ? 1
0) if ((handshake Handshake.RequestToSend
handshake Handshake.RequestToSendXOnXOff)
) SetDcbFlag(NativeMethods.FRTSCONTROL)
if (SetCommState(_handle, ref
dcb) false) handshake handshakeOld
SetDcbFlag(NativeMethods.FINX, fInOutXOld)
SetDcbFlag(NativeMethods.FOUTX, fInOutXOld)
SetDcbFlag(NativeMethods.FOUTXCTSFLOW,
fOutxCtsFlowOld) SetDcbFlag(NativeMethods.FRTS
CONTROL, fRtsControlOld) InternalResources.Win
IOError()
154string num 234-567-8901 using (SerialPort
mySerialPort new SerialPort(COM1, 14400))
mySerialPort.DtrEnable true mySerialPort.Op
en() mySerialPort.Write(ATDT num
Environment.NewLine) mySerialPort.Close()
Dim Incoming as String Using (Dim sp As New
SerialPort (COM1, 14400)) Incoming
sp.Read() End Using
155public sealed class SerialPort Component
public int InfiniteTimeOut -1 public int
BaudRate get set public int PortName
get set public int DataBits get set
public StopBits StopBits get set public
Encoding Encoding get set public int
ReadTimeout get set public int
WriteTimeout get set public Stream
BaseStream get public int
ReceivedBytesThreshold get set public
bool CDHolding get public bool CtsHolding
get public bool DsrHolding get public
SerialPort () public SerialPort (string
port) public SerialPort (string port, int
baudRate) public void Open() public void
Close() public int ReadChar() public void
Write(string value) public event
SerialPinChangedEventHandler PinChangedEvent pub
lic event SerialErrorEventHandler ErrorEvent
156(No Transcript)
157What we showed you
- How to identify a complex design
- How to use scenarios to fix the design
158The Pit of Success
Brad Abrams
159The Pit of Success in stark contrast to a
summit, a peak, or a journey across a desert to
find victory through many trials and surprises,
we want our customers to simply fall into winning
practices by using our platform and frameworks.
To the extent that we make it easy to get into
trouble we fail.- Rico Mariani
160Is using your framework correctly like
Climbing a mountain?
161Is using your framework correctly like
Scaling a peak?
162Is using your framework correctly like
Running across a desert?
163Is using your framework correctly like
Falling into a pit?
164Enable the Pit of Success by
- Avoiding the perilous summit of complexity
- And the desert of confusion
165- Make the simple things simple and the hard things
possible
166Exceptions and the Pit of Success
- Cleaner, more elegant, and wrong.
- Raymond Chen (http//blogs.msdn.com/oldnewthing/)
167When to throw an Exception?
- 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
168What Exception 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 //some operationcatch
(FileNotFoundException fe) //do some set of
workcatch (DriveNotFoundException be)
//do some other set of work
169Throwing an Exception
- Do not just map error codes onto 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)
- Dont expose privacy related information (such as
file paths)
170Performance
- Minimize the number of exceptions you throw in
your APIs success code-paths - You dont 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 a means of
control flow - Consider providing a way to avoid an exception
being thrown
171Performance (continued)
int itry i Int32.Parse(123) catch
(FormatException ) Console.WriteLine
(Invalid)
int iif (!Int32.TryParse (123, out i))
Console.Writeline(Invalid)
172Managing Resources during Exception Handling
- 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
173Managing Resources during Exception Handling
try . . .catch (DivisionByZeroException e)
// do clean up work throw new
BetterException (message, e)
- You may catch exceptions to re-throw them with a
clearer name - Typical at an API boundary
- Always nest the underlying exception
- Catch-and-rethrow has many of the benefits as
try..finally - But, be aware of debugging issues with
catch..throw new() and catch..throw - Generally, cleanup code should go in finalizer
174Catching Exceptions
- Do not catch and eat exceptions
- Exceptions should be handled only where there is
enough context to do the right thing - That generally means exceptions should be caught
as high in the application as possible - Mistake catch the exception, report the error
and rethrow it. - Only catch where you can handle it
- Mistake catch the exception, turn it into a
bool pass/fail and return the boolÂ
175Catching Exceptions
- Consider including a try/catch at the top of a
threads stack if the error can be handled
properly - Unhandled exceptions at the top of the main
thread will terminate the app - In 2.0, unhandled exceptions at the top of the
stack on any thread will terminate the app - But avoid catch blocks in finalizers
- Be aware In many cases it is appropriate to
let the app terminate
176Catching Exceptions
- Be aware of (but ignore) exceptions that dont
inherit from System.Exception - Allowed in V1.0\V1.1, addressed in V2.0
- See UnhandledException event on AppDomain
177The perilous summit of complexity by example
178How do I read all the lines from a file?
179VS7 Era
IO seems like a reasonable place to start
180Why did they make it inaccessible Backup, and try
again
181Open.. Looks like a good first step
182Hmm OK, what do I do with a FileStream?
183Ok good, synchronous and asynchronous
operations.. What the heck is that?
184I think I am in the wrong place..
185Back up, lets try a different type
186Ahh, ReadLine(), this looks more promising..
187OK, How do you find the end?
188Thanks goodness there was a sample
189The pit of success way
- developers fall into doing things the right way
190Just what I need
191Ah, a string I know just what to do with that
192How simple!
193Enable the Pit of Success by
- Avoiding the perilous summit of complexity
- And the desert of confusion
194Addition Through Subtraction
10
5
15
195Addition Through Subtraction
- Add value by subtracting features
- And the corollary
- Remove value by adding features
- More features ! More Value
196Addition Through Subtraction By Example
- CLR does not support multiple inheritance
- No Set class in System.Collections
- No String.OpenFile() method
- What can you cut from your API to make it more
valuable?
197Addition Through Subtractionand Danger of
over-design
198API Design Theater II
- The Main Character
- Bright young developer
- The Setting
- Her first big project
- The Setup
- Create a class that models a car
- Actions required Start and Drive
199Design Pass One Meets Requirements
- Pass one meets requirements
200Design Pass Two More than Enough
201Design Pass Three Way too much
202Time to Ship
Time to cut
203What we ship Too much and not enough
204V.Next Worse Yet
- Now we want to add Color and Model, and we know
exactly how - But it is much harder because the design is ½
done and mostly wrong
205The moral
- Do as little as possible now (but no less) to
ensure room for extensibility in the future
206Some Specifics Guidelines on Designing for
Extensibility
207Abstract and Base classes
- Prefer broad, shallow hierarchies
- Less than or equal to 2 additional levels Rough
rule! - Contracts and responsibilities are difficult to
maintain and explain in deep complex hierarchies - Consider making base classes not constructible
(i.e. use Abstract classes) - Make it clear what the class is for
- Provide a protected constructor for subclasses to
call - System.Exception should not have had a public
constructor
208Virtual Method Example
public class TheBase Object public
override string ToString() return Hello
from the Base"
public class Derived TheBase public
override string ToString() return Hello
from Derived"
209Virtual Methods
Derived d new Derived()Console.WriteLine
(d.ToString())
TheBase tb dConsole.WriteLine (tb.ToString())
Object o tbConsole.WriteLine (o.ToString())
210Virtual Methods
- They all output Hello from Derived. Why?
- Method call virtualizes at runtime
- The static type doesnt matter
- This is the danger and power of virtual methods
- Danger Owner of base classes cannot control what
subclasses do - Power Base class does not have to change as new
subclasses are created
211Overriding
- Dont change the semantics of member
- Follow the contract defined on the base class
- All Virtual members should define a contract
- Dont require clients to have knowledge of your
overriding - Should you call the base?
212Virtual and non-virtual
- Use non-virtual members unless you have
specifically designed for specialization - Have a concrete scenario in mind
- Write the code!
- Think before you virtualize members
- References to base types must work with derived
types without knowing the difference - Must continue to call in the same order and
frequency - Cannot increase or decrease range of inputs or
output - See the Liskov Substitution Principle
213Interfaces versus Base Classes
- Favor using base classes over interfaces
- Base classes version better in general
- Allows adding members
- Members can be added with a default
implementation - Avoids incompatibilities common in ActiveX
- Interfaces are good for versioning behavior
(changing semantics)
214Interface Usage
public interface IComparable int
CompareTo(object obj)
- Interfaces are useful!
- Solves the multiple root problem
- The smaller, more focused the interface the
better - 1-2 members are best
- But interfaces can be defined in terms of other
simpler interfaces - Examples IComparable, IFormattable
215- The great proof of madness is the disproportion
of one's designs to one's means. Napoleon
Bonaparte
216Exercises
217Exercise Why Exceptions?
- Which of the following are good reasons to use
error codes rather than exceptions? - To avoid the base level overhead exceptions add
to the system - I have always used error codes
- It is easier to ignore errors when dealing with
error codes - Error codes are easier to localize than exceptions
218Exercise Creating your own Exceptions
- What is wrong with this picture?
-
PrinterOutOfRedTonerExceptionPrinterOutOfBlackAnd
WhiteTonerExceptionPrinterOutOfBlueTonerException
PrinterOutOfGreenTonerExceptionPrinterOnFireExce
ptionPrinterOutOfPaperInTrayOneExceptionPrinterO
utOfPaperInTrayTwoExceptionPrinterOutOfPaperInTra
yThreeExceptionPrinterOutOfPaperInTrayFourExcepti
onPrinterOutOfPaperInTrayFiveException
219Exercise Handling Exceptions
- Where is the best place to handle exceptions?
- Close to where it is thrown
- At the top level, where you have the most context
to handle - No where, exceptions are scary
- Early and often
220Exercise Handling Exceptions
- What is the main exception handling problem with
this code?
try CallAMethod() CallAnotherMethod()
catch (Exception ) CallAThirdMethod()
221Exercise Virtual Methods
- Mark the following statements true or false
- (True/False) Implementations of virtual methods
can be effectively replaced by subclasses - (True/False) The Runtime insures that overridden
methods call their base implementations - (True/False) Make as many members virtual as
possible to allow for easy extensibility and
versioning
222Summary
- Allow developers to fall into the pit of success
using your framework by avoiding - The Perilous Summit of complexity
- Have the right things get noticed
- Create the right levels of abstraction
- The Desert of Confusion
- Add value by removing features
- Danger of over design
- Simple contracts are better
223API Design ExperienceDesign your Own
- Krzysztof Cwalina
- Brad Abrams
224Design E-Mail Component
- Requirements
- Send simple text messages
- Send messages with attachments
- Simplicity is the main objective
225Logistics
- Work in pairs
- Write the design on a piece of paper or on your
laptop - If you want feedback
- Send the design to kcwalina_at_microsoft.com or
- Write your email above the design and leave the
paper on your chair. - You have 20 minutes
226Design Process
- Remember Design Process
- Samples
- API Specification
227Example Scenario Samples
228Example API Specification
229Design Experience Summary
- Design an e-mail component
- Send simple text messages
- Send messages with attachments
- Simplicity is the main objective
- Work in pairs
- Write on a piece of paper or your laptop
- Scenario Samples
- API Specification
- If you want feedback
- email us at kcwalina_at_microsoft.com
- Write your email above the design and leave the
paper on your chair.
230In Closing
- Brad Abrams
- Krzysztof Cwalina
231Four Keys of Framework Design
232The Power of Sameness
- Influence of expectations
- Naming conventions and common suffixes\prefixes
- Habits win out over the special cases
- Common Exception pattern
- With great power comes great responsibility
- Method overloading
- Meet developers where they are
- constructors and properties pattern teaches us to
meet developers expectations
233Framework Design Matters
- Framework design does not happen magically
- Best frameworks are designed upfront by framework
designers - There are several qualities of a well-designed
framework that require focused framework design
process
234Tools for Communication
- Dont force your consumers to be archeologists
digging for how to use your framework - Every element in your design has a specific
meaning Know them and use them correctly - Avoid the common mistakes
- Make brand new mistakes
235The Pit Of Success
- Allow developers to fall into the pit of success
using your framework by avoiding - The Perilous Summit of complexity
- Have the right things get noticed
- Create the right levels of abstraction
- The Desert of Confusion
- Add value by removing features
- Danger of over design
- Simple contracts are better
236Feedback
- We want your feedback, please take time to fill
out the survey
And please blog
237Resources
Framework Design Guidelines Conventions, Idioms,
and Patterns for Reusable .NET
Libraries Krzysztof Cwalina, Brad Abrams Signing
now or at the bookstore Wed 115-145
- http//www.gotdotnet.com/team/fxcop/
- Brad Abrams
- brada_at_microsoft.com
- http//blogs.msdn.com/brada
- Krzysztof Cwalina
- kcwalina_at_microsoft.com
- http//blogs.msdn.com/kcwalina