Implementing .NET Framework Interfaces

Exam objective: 70-536 - Implement .NET Framework interfaces to cause components to comply with standard contracts. (Refer System namespace)

In object oriented programming, an interface is an abstract set of constants and methods used to define a common way to access a specific feature set of an object. Interfaces are abstract types and cannot be instantiated. Instead, they are inherited by classes which provide the implementation for the interface methods. Multiple interfaces may be inherited by a single class.

The .NET 2.0 Framework defines several generic, type-safe interfaces that are used to implement common features found in .NET classes. These interfaces are expected to be present for objects to work with specific features, such as sorting, type conversions, and resource disposal. All of these interfaces are located in the System namespace. The ones to know for the 70-536 exam are:

  • System.ICloneable
  • System.IComparable
  • System.IConvertible
  • System.IDisposable
  • System.IEquatable
  • System.IFormattable

Note: The System.INullableValue interface has been removed from the .NET 2.0 Framework because nullable types are now an intrinsic type in the .NET Common Language Runtime (CLR).

See Also

ICloneable Interface

The System.ICloneable interface is used when an object must support copying (cloning) of itself. The resulting copy will be the same type, or a compatible type, and contain all of the values of the original instance.

The ICloneable interface only has one method, Clone, which is called to create and return a copy of the current object instance. The Clone method is overridden by the class and its definition implements the actual cloning logic. The cloning may be implemented as a deep copy (all contained objects are duplicated) or a shallow copy (all contained objects are references to the original object).

The following C# code example illustrates a class implementing the ICloneable interface. The Clone method creates a copy by instantiating its object type and then copying its member values. A shallow copy of alValue is made using the Clone method of the ArrayList object.

using System;
using System.Collections.Generic;

public class MyClassCloneable : ICloneable
{
    private int intValue;
    private string strValue;
    private ArrayList alValue;

    public MyClassCloneable(int ival, string sval)
    {
        intValue = ival;
        strValue = sval;
        alValue = new ArrayList();
    }

    // Overrise Clone to provide the actual object instantiation
    // and value copying operations.
    public object Clone()
    {
        MyClassCloneable copy =
            new MyClassCloneable(this.intValue, this.strValue);
        copy.alValue = (ArrayList)alValue.Clone();

        return copy;
    }
}
See Also

IComparable Interface

The System.IComparable interface must be implemented by any class that requires sorting, usually because its instances will be stored in Arrays or ArrayList objects. The Sort method requires that an IComparable interface be present in the objects that are being sorted. Objects are compared based on type values (usually string or numeric) that they contain.

The IComparable interface only has one method, CompareTo, which is called to perform the object comparison. The CompareTo method is overridden by the class and its definition implements the value comparison logic. The return value will indicate the object being compared is greater than (>0), less than (<0), or equal to (0) the object performing the comparison.

The following C# code example illustrates a class implementing the IComparable interface. The CompareTo methods returns a value based on the comparison of the strValue member value.

using System;

public class MyClassComparable : IComparable
{
    private int intValue;
    public  string strValue;

    public MyClassComparable(int ival, string sval)
    {
        intValue = ival;
        strValue = sval;
    }

    // Override CompareTo to implement comparison logic
    public int CompareTo(object obj)
    {
        // Check if the object passed in is the same type
        if (obj is MyClassComparable)
        {
            MyClassComparable temp = (MyClassComparable)obj;
            // Use the CompareTo method of the string object
            // to make the actual comparison
            return this.strValue.CompareTo(temp.strValue);
        }
        throw new ArgumentException("obj is not a MyClassComparable");
    }
}

See Also

IDisposable Interface

The System.IDisposable interface is implemented when a class may allocate external, unmanaged resources that cannot be released by the garbage collector. Such resources include memory buffers, disk files, and database connections. In this case, the object’s public Dispose() method must be explicitly called called before the object itself is destroyed to free the unmanaged resources.

The IDisposable interface only has one method, Dispose, which is called when the resources of the object are to be freed for garbage collection. The Dispose method is overridden by the class and its definition implements the code that frees both managed and unmanaged resources.

The following C# code example illustrates a class implementing the IDisposable interface. Note that the .NET garbage collector is not aware of the IDisposable interface, so it must be told explicitly not to finalize the object if its resources have already been disposed:

using System;
using System.Runtime.InteropServices;

public class MyClassDisposable : IDisposable
{
    private GCHandle handle;      // Handle external unmanaged resource
    private byte[] buffer = new byte[1000];// External unmanaged buffer

    private byte[] buffer2 = null;         // Local managed buffer

    // Flag indicating this object has been disposed
    private bool disposed = false;

    public MyClassDisposable()
    {
        // Create a buffer from external unmanaged memory
        this.handle = GCHandle.Alloc(this.buffer, GCHandleType.Pinned);
        // Create a buffer from local managed memory
        this.buffer2 = new byte[1000];
    }

    //Implement public IDisposable interface
    public void Dispose()
    {
        // Perform the actual resource disposal
        Dispose(true);

        // Tell the GC it does not need to finalize this object
        // by calling the destructor
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Free the local managed resources
            buffer2 = null;
        }
        // Free the external unmanaged resources
        handle.Free();
        buffer = null;

        disposed = true;
    }

    // Use C# destructor syntax for finalization code. The destructor is
    // only called by the GC if the object's Dispose method is not called.
    ~MyClassDisposable()
    {
        // Call Dispose with false, causing only the unmanaged resources to be freed
        Dispose(false);
    }
}

See Also

IEquatable Interface

The System.IEquatable interface is used to compare two instances of an object for equality. The objects are compared based on the logic implemented in the class. The comparison results in a boolean value indicating if the objects are different. This is in contrast to the System.IComparable interface, which return an integer indicating how the object values are different.

The IEquatable interface declares two methods that must be overridden. The Equals method contains the implementation to perform the actual comparison and return true if the object values are equal, or false if they are not. The GetHashCode method should return a unique hash value that may be used to uniquely identify identical objects that contain different values. The type of hashing algorithm used is implementation-specific.

The following C# code example illustrates a class implementing the IEquatable interface:

using System;

public class MyClassEquatable : IEquatable<MyClassEquatable>
{
    private int intValue;
    private string strValue;

    public MyClassEquatable(int ival, string sval)
    {
        intValue = ival;
        strValue = sval;
    }

    // Override the GetHashCode method to provide a hash
    // of this object based on all of its values
    public override int GetHashCode()
    {
        return intValue.GetHashCode() ^ strValue.GetHashCode();
    }

    // Override the equals method to provide object comparison
    public override bool Equals(object obj)
    {
        return Equals((MyClassEquatable)obj);
    }

    // Perform the actual value comparisons
    public bool Equals(MyClassEquatable obj)
    {
        // Verify compatibility and is not null
        if (obj is MyClassEquatable == false)
            return false;

        // Verify an identical run-time type
        if (this.GetType() != obj.GetType())
            return false;

        if (this.intValue != obj.intValue)
            return false;

        if (this.strValue != obj.strValue)
            return false;

        return true;
    }

    // Override the equals operator to provide object comparison
    public static bool operator ==(MyClassEquatable obj1,
                                   MyClassEquatable obj2)
    {
        return obj1.Equals(obj2);
    }

    // Override the not equals operator to provide object comparison
    public static bool operator !=(MyClassEquatable obj1,
                                   MyClassEquatable obj2)
    {
        return !obj1.Equals(obj2);
    }
}

See Also

IFormattable Interface

The System.IFormattable interface is used to provide a method which returns a representation of an object as a string. The IFormattable interface only has one method, ToString, which is called to create and return the string value. The ToString method is overridden by the class and its definition implements the actual string-conversion logic.

The following C# code example illustrates a class implementing the IFormattable interface:

public class MyClassFormattable : IFormattable
{
    private int intValue;
    private string strValue;

    public MyClassFormattable(int ival, string sval)
    {
        intValue = ival;
        strValue = sval;
    }

    public override String ToString()
    {
        return ToString(null, null);
    }

    // Provide the actual code to deterne the format of the string
    // return based on the format parameter
    public String ToString(String format, IFormatProvider fp)
    {
        // Return just the intValue value as a string
        if (format == "intValue")
            return intValue.ToString();

        // Return just the strValue value as a string
        if (format == "strValue")
            return strValue;

        // If no recognized format is specified, so return
        // the value using the default string format
        return String.Format("({0}, {1})", intValue, strValue);
    }
}

See Also

IConvertible Interface

The System.IConvertible interface is used convert to an implementation-specific value or reference of a data type to an equivalent value or reference that is known to the .NET common language runtime (CLR). The IConvertible interface is used by the .NET Convert class to return the specified type value of the object.

The IConvertible interface has 17 methods, all of which must be overridden to provide type conversions for objects value or reference. These methods are GetTypeCode, ToBoolean, ToSByte, ToByte, ToInt16, ToUInt16, ToInt32, ToUInt32, ToInt64, ToUInt64, ToSingle, ToDouble, ToDecimal, ToDateTime, ToChar, ToString, and ToType. Each of these methods will contain the code implementing the conversion of the specific data type, or will throw an InvalidCastException to indicate that no such conversion exists.

The following C# code example illustrates a class implementing the IConvertible interface. The value is a four-byte array that may be converted to any unsigned data type that is at least four bytes in size. Note that each method may be called directly and without using the Convert class:

public class MyClassConvertible : IConvertible
{
    // A pixel contains four byte values
    private byte[] pixelValue;

    public MyClassConvertible(byte red, byte green, byte blue, byte alpha)
    {
        pixelValue = new byte[] { blue, green, red, alpha };
    }

    public TypeCode GetTypeCode()
    {
        return TypeCode.Object;
    }

    object IConvertible.ToType(Type conversionType, IFormatProvider provider)
    {
        return Convert.ChangeType(pixelValue, conversionType, provider);
    }

    bool IConvertible.ToBoolean(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }

    byte IConvertible.ToByte(IFormatProvider provider)
    {
        return Convert.ToByte(pixelValue);
    }

    char IConvertible.ToChar(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }

    DateTime IConvertible.ToDateTime(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }

    decimal IConvertible.ToDecimal(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }

    double IConvertible.ToDouble(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }

    short IConvertible.ToInt16(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }

    int IConvertible.ToInt32(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }

    long IConvertible.ToInt64(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }

    sbyte IConvertible.ToSByte(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }

    float IConvertible.ToSingle(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }

    string IConvertible.ToString(IFormatProvider provider)
    {
        // red:green:blue:alpha
        return string.Format("{0}:{1}:{2}:{3}",
            pixelValue[2], pixelValue[1], pixelValue[0], pixelValue[3]);
    }

    ushort IConvertible.ToUInt16(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }

    uint IConvertible.ToUInt32(IFormatProvider provider)
    {
        // alpha:red:green:blue
        return Convert.ToUInt32(pixelValue[0] + (pixelValue[1] << 8) +
            (pixelValue[2] << 16) + (pixelValue[3] << 24));
    }

    ulong IConvertible.ToUInt64(IFormatProvider provider)
    {
        // alpha:red:green:blue
        return Convert.ToUInt64(pixelValue[0] + (pixelValue[1] << 8) +
            (pixelValue[2] << 16) + (pixelValue[3] << 24));
    }
}

See Also

Leave a Reply