Brad Abrams
Good discussion on the first two.... Let's see how this goes.
Fields
?FONT face="Times New Roman" size=1 Never use publicly exposed instance fields
?FONT face="Times New Roman" size=1 Properties offer more flexibility at minimal cost
?FONT face="Times New Roman" size=1 JIT inlines simple property access
?FONT face="Times New Roman" size=1 Easy to add cache or delay creation in the future
?FONT face="Times New Roman" size=1 For static fields, do not include a prefix on a public field name
?FONT face="Times New Roman" size=1 Example: 'g_' or 's_' to distinguish static vs. non-static fields
const
?FONT face="Times New Roman" size=1 Compile-time evaluation
?FONT face="Times New Roman" size=1 Stable across versions
?FONT face="Times New Roman" size=1 Always static
readonly
?FONT face="Times New Roman" size=1 Run-time evaluation
?FONT face="Times New Roman" size=1 Unstable across versions
?FONT face="Times New Roman" size=1 Static or instance
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(...);
}
Properties
?FONT face="Times New Roman" size=1 Smart fields
?FONT face="Times New Roman" size=1 Calling syntax like fields
?FONT face="Times New Roman" size=1 Flexibility of methods
?FONT face="Times New Roman" size=1 Use read only properties where appropriate
?FONT face="Times New Roman" size=1 Do not use write-only properties
?FONT face="Times New Roman" size=1 Consider raising PropertyChanged events
?FONT face="Times New Roman" size=1 Property getters should be simple and therefore unlikely to throw exceptions
?FONT face="Times New Roman" size=1 Properties should not have dependencies on each other
?FONT face="Times New Roman" size=1 Setting one property should not affect other properties
?FONT face="Times New Roman" size=1 Properties should be settable in any order
?FONT face="Times New Roman" size=1 Common to have read-only public access and protected write access
public class MyClass {
private string name;
public string Name {
get {
return name;
}
}
protected void SetName (string name)
{
this.name = name;
}
}
Properties versus Methods
?FONT face="Times New Roman" size=1 Do use a property:
?FONT face="Times New Roman" size=1 If the member has a logical data member
string Name {get;} //Good
string GetName() //Bad
Guid GetNext(){} //Good
Guid Next {get;} //Bad
?FONT face="Times New Roman" size=1 Do use a method:
?FONT face="Times New Roman" size=1 If the operation is a conversion, such as ToString()
?FONT face="Times New Roman" size=1 If the getter has an observable side effect
?FONT face="Times New Roman" size=1 If order of execution is important
?FONT face="Times New Roman" size=1 If the method might not return immediately
?FONT face="Times New Roman" size=1 If the member returns an array
EmployeeList l = FillList();
for (int i = 0; i < l.Length; i++) {
if (l.All[i] == x){...}
}
//calling code:
if (l.GetAll()[i]== x) {...}
?FONT face="Times New Roman" size=1 Creates a new snap shot of the array each time through the loop
?FONT face="Times New Roman" size=1 The GetAll() form makes this much clearer
Properties: Indexers
?FONT face="Times New Roman" size=1 Use if the logical backing store is an array
?FONT face="Times New Roman" size=1 Almost always int or string indexed
?FONT face="Times New Roman" size=1 Rare to have multiple indices
public char this[int index] {get;}
String s = "foo";
Console.WriteLine (s[i]); // calls indexer
Events
?FONT face="Times New Roman" size=1 Defining an event
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);
}
}
?FONT face="Times New Roman" size=1 Using an Event
void Initialize() {
Button b = new Button(...);
b.Click += new EventHandler(ButtonClick);
}
void ButtonClick(object sender, EventArgs e)
{
MessageBox.Show("You pressed the button");
}
?FONT face="Times New Roman" size=1 Events are raised, not triggered or fired
?FONT face="Times New Roman" size=1 Name events with a verb
?FONT face="Times New Roman" size=1 E.g.: Click, Paint, DrawItem, DropDown,
?FONT face="Times New Roman" size=1 Event handlers have void return type
public delegate void MouseEventHandler (
object sender,
MouseEventArgs e);
?FONT face="Times New Roman" size=1 Event handler delegates use a signature that follows the event design pattern
?FONT face="Times New Roman" size=1 Use strongly typed event data where appropriate
public class MouseEventArgs :
EventArgs { }
?FONT face="Times New Roman" size=1 Able to add new members without a breaking change
?FONT face="Times New Roman" size=1 Provide a protected method to raise the event
?FONT face="Times New Roman" size=1 Named OnEventName
?FONT face="Times New Roman" size=1 Make it virtual if extensibility is needed
public class Button {
private ButtonClickHandler onClickHandler;
protected void OnClick (ClickEventArgs e) {
if (onClickHandler != null) {
// call the delegate if non-null
onClickHandler(this, e);
}
}
}
?FONT face="Times New Roman" size=1 Events are callbacks into arbitrary user code
?FONT face="Times New Roman" size=1 Do not assume anything about the state of your object
?FONT face="Times New Roman" size=1 Code defensively
protected void DoClick() {
PaintDown(); // paint button in depressed state
try {
OnClick(); // call event handler
}
finally {
// window may be deleted in event handler
if (windowHandle != null) {
PaintUp(); // paint button in normal state
}
}
}
Static Members
?FONT face="Times New Roman" size=1 Any kind of member can be static
?FONT face="Times New Roman" size=1 Static members
?FONT face="Times New Roman" size=1 Cannot access instance state
?FONT face="Times New Roman" size=1 Cannot override or specialize
?FONT face="Times New Roman" size=1 Should be thread-safe
?FONT face="Times New Roman" size=1 Commonly used for
?FONT face="Times New Roman" size=1 Singleton pattern
?FONT face="Times New Roman" size=1 Utility methods
?FONT face="Times New Roman" size=1 Statics are the .NET equivalent of global variables or global functions
?FONT face="Times New Roman" size=1 Not object oriented
?FONT face="Times New Roman" size=1 Same evils as global
?FONT face="Times New Roman" size=1 But can be very useful, e.g., System.Math
Singleton Pattern
?FONT face="Times New Roman" size=1 Ensures that a class has only one instance and provide a global point of access to it
?FONT face="Times New Roman" size=1 This is not exactly the GoF pattern (see threading section)
public sealed class DBNull {
private DBNull() {}
public static readonly DBNull Value = new DBNull();
// instance Methods...
}
//Calling code
if (x == DBNull.Value) {..}
?FONT face="Times New Roman" size=1 Notice this class:
?FONT face="Times New Roman" size=1 Is sealed to prevent sub-classing to add instances
?FONT face="Times New Roman" size=1 The Value is static for easy access
?FONT face="Times New Roman" size=1 The Value is readonly so it cannot be modified
?FONT face="Times New Roman" size=1 Has a private constructor
?FONT face="Times New Roman" size=1 The instance is immutable
?FONT face="Times New Roman" size=1 No methods that can mutate its state
Parameter Passing
?FONT face="Times New Roman" size=1 Value types and Reference types can both be passed by value or by reference
?FONT face="Times New Roman" size=1 A value type by value copies the value
?FONT face="Times New Roman" size=1 No side effects
?FONT face="Times New Roman" size=1 Commonly used
public int Add (int x, int y) {..}
?FONT face="Times New Roman" size=1 A value type by reference uses a pointer to the value
?FONT face="Times New Roman" size=1 Side effects possible
?FONT face="Times New Roman" size=1 Rarely used
public static int Exchange (ref int location, int value) {..}
?FONT face="Times New Roman" size=1 A reference type by value copied the reference
?FONT face="Times New Roman" size=1 Side effects are possible on mutable types
?FONT face="Times New Roman" size=1 Commonly used
public void Insert (object value) {..}
?FONT face="Times New Roman" size=1 A reference type by reference uses a pointer to the reference variable
?FONT face="Times New Roman" size=1 Side effects possible
?FONT face="Times New Roman" size=1 Almost never used
public static int Method (ref object moreData) {..}
?FONT face="Times New Roman" size=1 Using Ref and Out parameters
?FONT face="Times New Roman" size=1 Primarily used for interop
?FONT face="Times New Roman" size=1 Avoid directly exposing publicly
?FONT face="Times New Roman" size=1 May be used for extremely performance-sensitive areas
?FONT face="Times New Roman" size=1 Almost exclusively used with value types
?FONT face="Times New Roman" size=1 Ref is a CLR feature
?FONT face="Times New Roman" size=1 Out is a C# feature
?FONT face="Times New Roman" size=1 Out parameter semantics downgrade to Ref semantics in other languages
?FONT face="Times New Roman" size=1 Ref and out designs are less usable
public void GetLocation (
ref int x, out int y) {..}
//calling code
int x = 42;
int y;
b.GetLocation (ref x, out y);
public struct Point {
public int X {get;}
public int Y {get;}
}
//calling code
Point p = b.Location;
Argument Validation
?FONT face="Times New Roman" size=1 Do argument checking on every publicly exposed member
?FONT face="Times New Roman" size=1 Catches errors early (fail-fast)
?FONT face="Times New Roman" size=1 Much easier to debug
?FONT face="Times New Roman" size=1 Powerful security precaution
?FONT face="Times New Roman" size=1 Throw meaningful exceptions
?FONT face="Times New Roman" size=1 Subclasses of ArgumentException
public int Count {
get {return count;}
set {
if (value < 0 || value >= MaxValue)
throw new ArgumentException(..);
}
}
public void Select (int start, int end) {
if (start < 0)
throw new ArgumentException(..);
if (end < start)
throw new ArgumentException(..);
}