Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to expand .NET structs and sealed classes in C#

0.00/5 (No votes)
1 Aug 2006 1  
A way for expanding sealed data structures in .NET

Introduction

Sometimes it is required to add some functionality to existing data structures. The OOP school's way for this is to use inheritance. In situations where we consume interfaces instead of class or structure types we can extend the interface easily. In some languages like Ruby data structures are never sealed. So we can add extra abilities of our objects to the processing workflow of the running engine of the language. However, if the data structures are sealed then there could be no way to do this.

Here we'll see a way to convert two types implicitly that 'simulates' inheritance and when needed our type will be cast internally by the CLR.

Background

When I was studying a few Ruby code snippets I encountered this idea and decided to implementing it in C#. In some scenarios this is very handy. Ruby programmers use this feature in their daily developments. But there is a bit of caution here: this method can be Aggregation or a Composition, and if you are going to extend a complex sealed type in .NET, you must be aware of how it is disposed (Implementing of IDisposable is a formal way for doing this). Because the wrapped type does not release it's captured resources, there may be some conflicts in execution path. So the finalization process must takes place properly.

Overall Process

For this we need to construct some facilities in our data structure that enables the CLR to 'infer' our target type from the context.
  1. Implicit overloaded operators for conversion

    Here we'll try to expand string class so this operations will be:

            public static implicit operator string(String V)
            {
                return V.mval;
            }
            public static implicit operator String(string n)
            {
                return new String(n);
            }
    //REQUIRED BECAUSE OF CONVERSION OPERATORS public override bool Equals(object obj) { return this.mval == (string)obj; } public override int GetHashCode() { return this.mval.GetHashCode(); }
    As you can see by browsing the provided code 'mval' is the internal place for
    keeping the object of target type which is 'string'. 'String' is our new type, and 'string' is the .NET standard type for the strings.

    In addition we have overloaded two other methods : Equals and GetHashCode. These implementations are needed by the CLR for performing the conversion.
     
  2. Providing implementations of necessary unary and binary operations.

    Here the only operation that is defined in .NET framework for strings in '+' which makes a new string of it's operands. Of course we can overload other operations in addition to '+' for our purpose.
     
  3. Providing implementation of comparison operators.

    This is in fact same as previous step and the separation of conceptual paragraph is only an emphasis on it's importance that needs more care.

After this we have done and we have a new type that is equivalent to standard string. We can use it to provide a new type 'email' that is a string with a special format or in any sort of data that will be presented in a formatted string.

Using the code

Let see through the code some practical points. This is main method that performs a simple test :
static void Main (string[] args)
{
    UpperCaseString ucs = "this Is A teST TeXt.";
    Console.WriteLine (ucs);
    Console.WriteLine (ucs << 8);
    string newString = ucs << 8 >> 1;
    Console.WriteLine (newString);
    //

    Console.WriteLine ("Press any key to continue ...");
    Console.ReadKey ();
}

UpperCaseString is an always upper case string class. As you see the instance of UpperCaseString has been initialized by an ordinary assignment. Then it is consumed by the WriteLine method of the Console class to print its value. There is no explicit conversion needed here.

There are new overloaded operators there : '>>' and '<<'. The first one shifts the string n characters to right and '<<' shifts the string n characters to left. Again there no necessary instructions for constructing a new instance of System.String named here newString. The result of '>>' and '<<' operations are automatically converted to the proper type (here System.String).

This was a little example of using this feature to attach additional functionalities and properties to existing sealed types. You can use the definition of UpperCaseString as a template for providing your specialized types. In addition you may want to provide some generic tools for performing needed applications.

Points of Interest

It is interesting how can this option affects current implementation scenarios on the .NET plateform. We can name some of them by re-implementing required features in new way. Maybe some sort of parsing texts and file processing or some of GUI data propagation and data gathering and so on. But the more interesting thing would be a case that is nicely fit this and this suited nicely to overcome the problem. Do you know one?

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here