65.9K
CodeProject is changing. Read more.
Home

Strongly Typed Guid Classes

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (11 votes)

Mar 23, 2004

2 min read

viewsIcon

45242

downloadIcon

497

Create strongly typed Guid classes by deriving (essentially) empty subclasses from a TypedGuid base class.

Introduction

This article outlines a simple programming tip to create strongly typed Guid classes with little effort. Code that uses typed Guids becomes more readable and improves compile time type safety for Guid instances.

Intent

Define a class to allow .NET Guids to be easily associated with a type.

Motivation

The abstraction of a unique object identifier is often realized by employing the .NET Guid structure. Each object becomes associated with a Guid and can then be uniquely identified. A typical example is the storage of objects into relational database tables, where the object's Guid represents the table's primary key. C# or VB.NET application code can use this Guid structure to represent references to objects.

public class Database
{
    static public Vendor LoadVendor(Guid guid)
    {
        // Only relevant parts shown.
    }
}

But there are some issues with this approach. The Guid structure is domain or type oblivious, it can represent any kind of object! A method expressed in terms of a Guid structure will provide little information to the reader or to the compiler on the type of object being represented. The opportunity to write expressive code or to detect errors at compile time is lost...

public class OrderData
{
    // Only relevant parts shown.
    public Guid TimeID    { get { return m_timeID; } }
    public Guid ProductID { get { return m_productID; } }
    public Guid VendorID  { get { return m_vendorID; } }
}

public class Order
{
   // Only relevant parts shown.
    private OrderData m_orderData;

    public Vendor GetVendor()
    {
        Guid guid = m_orderData.ProductID;
        return Database.LoadVendor(guid);
    }
}

If we could use a kind of "typed Guid" to represent domain concepts - for example VendorGuid or ProductGuid - then we would be able to profit from the benefits of understandable and compile time safe code.

Solution

Class-ify the Guid structure into a class to allow subclassing. The class-ified base class implements all Guid functionality while subclasses provide the association with domain types. The association is implied through the type of the subclass.

Structure

The TypedGuid class represents the class-ified Guid structure. It wraps a Guid instance and replicates most public methods available on the .NET Guid structure. It is an abstract base class, as creating instances of it would defeat its purpose. It provides a protected constructor that takes a Guid argument.

Strongly typed Guid classes are created by deriving subclasses from the TypedGuid class. The subclass constructor takes a single Guid argument and calls the base class constructor. In most cases the subclasses can be almost empty.

[Serializable]
public class VendorGuid : TypedGuid
{
    public VendorGuid(Guid g) : base(g)
    {
        // Empty body.
    }
}

The type of the subclass encapsulates the association with the domain type and can be used to detect errors at compile time.

Consequences

The TypeGuid class yields the following advantages and disadvantages:

  • The TypedGuid is a reference type whereas the Guid structure is a value type. The advantages that come with value types are lost on the TypedGuid.
  • TypedGuid is a first-class object and can be extended like any other object.
  • It is easy to add new TypedGuid instances to the system. In most cases it is sufficient to derive almost empty subclasses.
  • TypedGuid champions the CLR's aim of type safety.

Sample Code

The previous code re-expressed in terms of strongly typed Guid classes:

public class Database
{
    static public Vendor LoadVendor(VendorGuid vendorGuid)
    {
        // Only relevant parts shown.
    }
}

public class OrderData
{
    // Only relevant parts shown.
    public TimeGuid    TimeID    { get { return m_timeID; } }
    public ProductGuid ProductID { get { return m_productID; } }
    public VendorGuid  VendorID  { get { return m_vendorID; } }
}

public class Order
{
   // Only relevant parts shown.
    private OrderData m_orderData;

    public Vendor GetVendor()
    {
        ProductGuid guid = m_orderData.ProductID;
        return Database.LoadVendor(guid); // compiler will not allow this
    }
}