Amazon.com Widgets

On Designing Good Libraries -- Part II

You asked for it, you got it.. feedback always welcome.

 

 

 

Attribute Usage

         Suffix with “Attribute”

         Perf tip: seal attribute classes for faster runtime lookup

         Specify the AttributeUsage attribute completely

         Don’t rely on the defaults!

         Be as restrictive as possible

         You can always open it up later

[AttributeUsage(
      AttributeTargets.All,
      Inherited = true,
      AllowMultiple = false)]

 

         AttributeTargets – where is the attribute allowed to be applied?

         Inherited – should derived members/types be considered to have this attribute?

         AllowMultiple – Is it legal to put more than one instance of the attribute on a particular member?

 

         Use constructor arguments for required parameters (positional arguments)

         Provide a read-only property with the same name

         Use read-write properties for optional parameters (named arguments)

         Never use overloaded constructors

[AttributeUsage(AttributeTargets.All, AllowMultiple=true,
                   Inherited=false)]
public class NameAttribute : Attribute {
   public NameAttribute (string userName) {..}
   public string UserName {get {..}}
   public int Age {get {..} set{..}}
} //end class

 

Usage:

[NameAttribute("Bob", Age=87)]

 

UserName – positional argument

Age – named argument

 

Static Classes

         Static classes contain just static members

         Compromise between pure OO design with usability

         Commonly used for

         Shortcuts for other operations (System.IO.File)

         Functionality for which a full OO wrapper is unwarranted (System.Environment)

 

public sealed class Environment  {

  private Environment(){} //prevents creation

  public static void Exit(int exitCode) {..}

  public static int ExitCode {

    get {..}

    set {..}

  }

  public static string CommandLine {

    get {..}

  }

}

 

         Best used when:

         Clear charter for the class

         Not a “miscellaneous” bucket

         Not the center point of a design

         Use sparingly

         Watch out for disconnected design

         Static classes

         Are sealed

         Have private default constructor

         No instance members

         Static Classes: Bad Design

         Late in the final milestone we added a method to tell if the runtime is being shut down

         However we added the method as an instance method making it completely uncallable

         public sealed class Environment {
   private Environment() {} //Prevent creation
   // ---snip---
   public bool HasShutdownStarted {
      get { return nativeHasShutdown(); }
   }
   public static string UserName { get {...} }
   private static extern bool nativeHasShutdown();
   // ---snip---
}

          

 

Constructors

         Do minimal work in the constructor

         Only capture the parameters

         Cost is delayed

         You can throw exceptions from constructors

         Be consistent in the ordering and naming of constructor parameters

 

public class Foo  {
   private const string defaultA = "..";
   private const string defaultB = "..";
   public Foo():
      this(defaultA,defaultB) {}
   public Foo(string a):
      this(a, defaultB) {}
   public Foo(string a, string b) {
       /* do work here */
   }
}

 

         Many languages automatically add a public default constructor if you don’t specify any

         Abstract classes get a protected constructor

         These two code snippets are equivalent:

public class Foo {

}

public class Foo {

   public Foo () {}

}

 

         Always explicitly add a default constructor to avoid versioning issues

         Adding a new constructor removes the default one, breaking clients

// V1

public class Foo {

}

Calling Code works: Foo f = new Foo()

 

// V2

public class Foo {
  public Foo (int value)

}

Calling code breaks: Foo f = new Foo()

 

Method Usage

 

         Use overloading only when the overloads do semantically the same thing

         Incorrect overload:

String.IndexOf(string value) {}

String.IndexOf(char[] anyOf) {}

 

         Correct overload:

Convert.ToString(int value) {}

Convert.ToString(double value) {}

 

         Used to avoid boxing

        Write(object) works for any type

        But specialization avoids boxing

        Do only when completely special casing

public static void Write (bool value);
public static void Write (int value);
public static void Write (double value);
public static void Write (object value);

 

         Use appropriate default values

         Simple method assumes default state

         More complex methods indicate changes from the default state

MethodInfo Type.GetMethod (string name);
    //ignoreCase = false

MethodInfo Type.GetMethod (string name,
    boolean ignoreCase);

         Use a zeroed state for the default value (such as: 0, 0.0, false, “”, etc.)

         Be consistent in the ordering and naming of method parameters

         Only the method with the most parameters should be virtual if needed

public class Foo {
   private const string defaultForA = "a default";
   private const int defaultForB = 42;
   public void Bar(){
      Bar(defaultForA, defaultForB);
   }
   public void Bar (string a){
      Bar(a, defaultForB);
   }
   public /*virtual*/ void Bar (string a, int b){
      // core implementation here
   }
}

 

         Variable number of arguments (e.g. printf)

         Use params

public static string Format(string format,

     params object[] args);

 

         Not used for in/out params (e.g. scanf)

         Only provide overloads for performance reasons IF you special case each code path

 

void Format (string formatString, object arg1)

void Format (string formatString, object arg1, object arg2)

void Format (string formatString, params object [] args)

 

 

         Allowing method inlining by the JIT

         Minimize the use of virtual methods

         Don’t write really large methods

         Don’t have large numbers of locals

More to come...

Published 05 July 03 09:19 by BradA

Comments

# Roland Weigelt said on July 6, 2003 1:53 AM:
> Attributes: Never use overloaded constructors What's the reason/idea behind this? Roland
# Frans Bouma said on July 6, 2003 6:04 AM:
- "Late in the final milestone we added a method to tell if the runtime is being shut down. However we added the method as an instance method making it completely uncallable" I might get the wrong idea reading this, but... is this method ever tested? I mean, the developer who hammered in that code, did he/she ever run the method? I don't think so, because how would that be possible? Weird... - Does 'sealing' of classes get you any performance gains when using it on classes with solely static methods? - "Always explicitly add a default constructor to avoid versioning issues". This text is also in the library design guidelines in the .NET reference manual, and I first interpreted incorrectly: I thought I should add a default constructor ALWAYS, even if I already had a constructor in place which accepted parameters. Of course that's not necessary.
# Mattias Sjögren said on July 6, 2003 7:43 PM:
In the static classes section, it would be useful to mention the VB.NET Module type. Modules automatically gives you the behavior of a static class, except that it has no constructor, not even a private one, which I'd argue is even better. >Use a zeroed state for the default value (such as: 0, 0.0, false, “”, etc.) Isn't the zeroed state for a string null, not ""?
# Frank Hileman said on July 10, 2003 10:50 PM:
Also, to allow better inlining, don't inherit from MarshalByRef, or any of its derived classes: Component, Control, etc. Also Component is heavier than some people realize, and has a finalizer which may never be needed.
# Brad Abrams said on March 1, 2004 12:19 AM:
# Ram Prasad said on March 11, 2004 1:46 AM:
Does the usage of "Sealed" keyword in a virtual method will allow "JIT inlining"?
# Brad Abrams said on April 13, 2004 6:18 PM:
# Brad Abrams said on April 13, 2004 11:28 PM:
# C#deSamurai said on August 26, 2004 12:35 PM:
# Tutto fa .NET said on August 27, 2004 12:45 PM:
New Comments to this post are disabled

Search

This Blog

Syndication

Page view tracker