Good discussion on the first two.... Let's see how this goes.
Fields
• Never use publicly exposed instance fields
• Properties offer more flexibility at minimal cost
• JIT inlines simple property access
• Easy to add cache or delay creation in the future
• For static fields, do not include a prefix on a public field name
• Example: 'g_' or 's_' to distinguish static vs. non-static fields
const
• Compile-time evaluation
• Stable across versions
• Always static
readonly
• Run-time evaluation
• Unstable across versions
• 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
• Smart fields
• Calling syntax like fields
• Flexibility of methods
• Use read only properties where appropriate
• Do not use write-only properties
• Consider raising PropertyChanged events
• 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
• 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
• Do use a property:
• If the member has a logical data member
string Name {get;} //Good
string GetName() //Bad
Guid GetNext(){} //Good
Guid Next {get;} //Bad
• Do 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
EmployeeList l = FillList();
for (int i = 0; i < l.Length; i++) {
if (l.All[i] == x){...}
//calling code:
if (l.GetAll()[i]== x) {...}
• Creates a new snap shot of the array each time through the loop
• The GetAll() form makes this much clearer
Properties: Indexers
• Use if the logical backing store is an array
• Almost always int or string indexed
• Rare to have multiple indices
public char this[int index] {get;}
String s = "foo";
Console.WriteLine (s[i]); // calls indexer
Events
• 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); }
• 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");
• Events are raised, not triggered or fired
• Name events with a verb
• E.g.: Click, Paint, DrawItem, DropDown,
• Event handlers have void return type
public delegate void MouseEventHandler ( object sender, MouseEventArgs e);
• Event handler delegates use a signature that follows the event design pattern
• Use strongly typed event data where appropriate
public class MouseEventArgs : EventArgs { }
• Able to add new members without a breaking change
• Provide a protected method to raise the event
• Named OnEventName
• 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); } }}
• Events are callbacks into arbitrary user code
• Do not assume anything about the state of your object
• 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
• Any kind of member can be static
• Static members
• Cannot access instance state
• Cannot override or specialize
• Should be thread-safe
• Commonly used for
• Singleton pattern
• Utility methods
• Statics are the .NET equivalent of global variables or global functions
• Not object oriented
• Same evils as global
• But can be very useful, e.g., System.Math
Singleton Pattern
• Ensures that a class has only one instance and provide a global point of access to it
• 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) {..}
• Notice this class:
• Is sealed to prevent sub-classing to add instances
• The Value is static for easy access
• The Value is readonly so it cannot be modified
• Has a private constructor
• The instance is immutable
• No methods that can mutate its state
Parameter Passing
• Value types and Reference types can both be passed by value or by reference
• A value type by value copies the value
• No side effects
• Commonly used public int Add (int x, int y) {..}
• A value type by reference uses a pointer to the value
• Side effects possible
• Rarely usedpublic static int Exchange (ref int location, int value) {..}
• A reference type by value copied the reference
• Side effects are possible on mutable types
• Commonly used public void Insert (object value) {..}
• A reference type by reference uses a pointer to the reference variable
• Almost never usedpublic static int Method (ref object moreData) {..}
• Using Ref and Out parameters
• Primarily used for interop
• Avoid directly exposing publicly
• May be used for extremely performance-sensitive areas
• Almost exclusively used with value types
• Ref is a CLR feature
• Out is a C# feature
• Out parameter semantics downgrade to Ref semantics in other languages
• 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;}}
Point p = b.Location;
Argument Validation
• Do argument checking on every publicly exposed member
• Catches errors early (fail-fast)
• Much easier to debug
• Powerful security precaution
• Throw meaningful exceptions
• 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(..);}