Strongly-Typed Cloning

Problem
You’re going to implement the ICloneable interface. What methods should you have?

Solution
Implement two Clone methods: 1. A public, strongly-typed method that returns an instance of the current type. 2. A private method that implements the interface and returns a generic object.

Example

public class Account : ICloneable {
    // The public Clone method returns an Account object.
    public Account Clone() {
        return (Account) (this as ICloneable).Clone();
    }

    // The private Clone method returns a generic object.
    object ICloneable.Clone() {
        return this.MemberwiseClone();
    }
}

Implementing the ICloneable Interface

Problem
You need to clone a type. What should you do?

Solution
Implement the ICloneable interface for types that may need cloning.

Comments
If possible, make the Clone method public. Include documentation that calls out whether the Clone method performs a shallow or deep copy. You can simplify the Clone method by calling MemberwiseClone when returning a shallow copy of the object.

Example

public class Account : ICloneable {
    // Return a shallow copy of this Account object.
    public object Clone() {
        return this.MemberwiseClone();
    }
}

Private Interface Implementation in Inherited Types

Problem
You want to know how to implement private interface members in inheritable types.

Solution
Ensure that derived types can access and override nonpublic members in nonsealed classes.

Comments
In order to implement this scenario in C#, you need to define a protected virtual method and call it from the interface method.

Example

public class Account : IComparable {
    // Implement IComparable privately.
    int IComparable.CompareTo(object obj) {
        // Delegate to a protected virtual method.
        return CompareTo(obj);
    }

    // Protected method that derived types can override.
    protected virtual int CompareTo(object obj) {
        // Do the comparison here.
        ...
    }
}

Public & Private Members in Interfaces

Problem
You want to know if it’s bad practice to use private members in interfaces.

Solution
Even though there are several exceptions to the rule, you should prefer public interface members over private ones.

Comments
One reason to implement private interface members is when you don’t want types to contain many seldom-used public methods. A perfect example of this is numeric types in .NET implementing the IConvertible interface as private methods. Another exception is if you want to supply a public method that does the same thing as a private one but in a strongly-typed way.

Example

public class Account : ICloneable, IComparable {
    // ICloneable is a public implementation.
    public object Clone() {
        ...
    }

    // IComparable is a private implementation.
    int IComparable.CompareTo(object obj) {
        ...
    }
}

Naming Classes That Implement an Interface

Problem
You’re interested in guidelines for naming interfaces.

Solution
1. Use the Comparer suffix for types that implement the IComparer interface. Example: AccountComparer.
2. Use the Formatter suffix for types that implement the IFormatter interface. Example: AccountFormatter.
3. Use the interface name (without the I prefix) when defining a type whose only purpose is to implement an interface.

Comments
Let’s expound upon guideline #3 above. Here’s an example. If you have a class that implements the IWebDriver interface, you might name it BlazingFastWebDriver.

Immutable Interfaces

Problem
You want to know if it’s alright to change published interfaces.

Solution
You shouldn’t change interfaces in any way after publishing them to other developers.

Comments
Create a new interface that inherits from an existing one if you need to add members. Give the new interface a version number by appending it to the name. For example, IComponent2. Adding a version number highlights the relationship to the current interface.

Inheriting from MarshalByRefObject

Problem
You want to know when it’s appropriate to inherit from MarshalByRefObject.

Solution
Don’t inherit a new type from MarshalByRefObject or from types that derive from it. The only reason to do it is if you intend to use the type’s extra features.

Comments
It takes longer to instantiate types that derive from MarshalByRefObject. The JIT compiler can’t inline methods from these types, so method calls execute slower. A good recommendation is to split complex objects into two distinct types. Have one type expose the methods that don’t need features from MarshalByRefObject. This way, the JIT compiler can optimize calls to the object’s methods.

Strongly-Typed Equals and Compare Static Methods

Problem
You’re looking for a simplified way to compare instances of custom types.

Solution
Provide static, strongly-typed versions of the Compare and Equals methods.

Comments
You can make your code more robust by using methods that take arguments of a specific type. You should avoid taking in generic Object instances. Static methods are often preferable to instance ones. This is because you don’t need to check whether an object reference is null before calling the method.

Example
The String class exposes two overloads of the Equals static method. There’s the one inherited from Object, which takes two object arguments. There’s also the strongly-typed one that takes two strings. The String class also exposes several overloads of the Compare static method. These methods allow developers to test an entire string or a portion of it in both case-sensitive and case-insensitive modes.

public class Account {
    // In a real-world class, these would be properties.
    public string AccountName;
    public string AccountType;

    public Account(string accountName, string accountType) {
        this.AccountName = accountName;
        this.AccountType = accountType;
    }

    // The strongly-typed version of the static Equals method.
    public static bool Equals(Account objectOne, Account objectTwo) {
        if ( objectOne == null && objectTwo == null ) {
            // Two null objects are equal.
            return true;
        }
        else if ( objectOne == null || objectTwo == null ) {
            // They can't be equal if only one object is null.
            return false;
        }
        else {
            // Delegate to the String.Equals method if neither is null.
            return ( string.Equals(objectOne.AccountName, objectTwo.AccountName) &&
                String.Equals(objectOne.AccountType, objectTwo.AccountType) );
        }
    }

    // The strongly-typed version of the static Compare method.
    // Here we compare accounts on (AccountType, AccountName).
    public static int Compare(Account objectOne, Account objectTwo) {
        if ( objectOne == null && objectTwo == null ) {
            // Two null objects are equal.
            return 0;
        }
        else if ( objectOne == null ) {
            // A null object is less than any non-null object.
            return -1;
        }
        else if ( objectTwo == null ) {
            // A non-null object is greater than any null object.
            return 1;
        }
        else {
            // Delegate comparison to the String.Compare method.
            int result = String.Compare(objectOne.LastName, objectTwo.LastName);
            if ( result == 0 ) {
                result = String.Compare(objectOne.FirstName, objectTwo.FirstName);
                return result;
            }
        }
    }
}

// You can simplify the Equals method if a type exposes a static
// Compare method and performance is a non-issue.
public static bool Equals( Account objectOne, Account objectTwo) {
    return Compare(objectOne, objectTwo) == 0;
}

GetHashCode to Test Equality

Problem
You want to know if you should use the GetHashCode method to determine if two objects are equal.

Solution
Don’t use the value returned by the GetHashCode method to check if two objects are equal.

Comments
You can only come to one conclusion by comparing the hash codes of two objects. The conclusion is that objects with different hash codes are not the same. You never want to assume that two object variables with the same hash code point to the same object. This is because Object.GetHashCode is often overridden in derived types. The only version of the method you can rely on is in the Object class. The GetHashCode method’s implementation may change in future versions of the .NET Framework. This would cause types that don’t override GetHashCode to be potentially different.