.NET and me Coding dreams since 1998!

20Dec/060

Code better – Overriding the Object

The power of Object type is very often non utilized as much it should, so in this post I'll (try to) explain a few very simple principles of its smart use which makes my developer life much easier.

For my today post I'll use next simple class:

using System;

using System.Text;

 

namespace Test

{

    public class ClassOTO

    {

        private int _fieldA;

       

        public int PropertyA

        {

            get

            {

                return _fieldA;

            }

            set

            {

                if (_fieldA == value)

                    return;

                _fieldA = value;

            }

        }

 

        private string _fieldB;

 

        public string PropertyB

        {

            get

            {

                return _fieldB;

            }

            set

            {

                if (_fieldB == value)

                    return;

                _fieldB = value;

            }

        }

 

        private DateTime _fieldC;

 

        public DateTime PropertyC

        {

            get

            {

                return _fieldC;

            }

            set

            {

                if (_fieldC == value)

                    return;

                _fieldC = value;

            }

        }

 

 

    }

}

NOTE: I've seen a lot of time developers are groping in seperate regions type fields and in seperate regions type properties. I believe that is bad practice and that the field definition should be as close as possible to the property (when defining the property as a wrapper over the type field). That's how the the code is much more maintainable and instead of the swarms of field definitions without a clue who is using the field.


Class design

Object type exposes next virtual methods useful for overriding:

  • ToString()
  • Equal(object)
  • GetHashCode()

ToString()

I think is a good practice to override the ToString() method so it would enlist all the member values. That saves a lot of time by easing the use of the watch window for VS debugging

public override string ToString()

{

int capacity

              = _fieldA.ToString().Length

       + _fieldB.Length

              + _fieldC.ToString().Length;

 

StringBuilder sb = new StringBuilder(capacity);

       sb.Append(_fieldA);

       sb.Append(_fieldB);

       sb.Append(_fieldC);

 

return sb.ToString();

}

 

NOTE:   To understand the reasons of unnecessary the string builder capacity setting take a look at String Builder gotcha

By doing that override we are avoiding usage of this:

Normal watch window

and get the ability to use this

ToString watch window

 

Equals (object)

In case of reference type implementation of object equal virtual method the default implementation of the method is looking like:

public bool Equals(object obj)

{

if (this == obj) return true;

      return false;

}

 

which is not the exactly what we could expect in comparing our object with the given one.

In case of implementation of equals method by value based types (e.g. struct) the implementation is using reflection to compare all the instance members and return the boolean result.

This is a kind closer to the desired functionality by my standards but still relying on the reflection is bad performance related practice

So in both cases the default implementation of Equals method is not appropriate to the needs, so (by me) we should always override it with our custom implementation.

In case we have implemented the ToString method like presented, we could use that to implement Equals like this:

 

public override bool Equals(object obj)

{

if (obj == null) return false;

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

return this.ToString() == ((ClassOTO)obj).ToString();

}

First two checks are enough self explanatory. The third comparing of all instance members relies on the fact that we have already enlisted all the instance fields while implementing the ToString() method

GetHashCode()

If you would try to compile now ClassOTO VisualStudio would throw

CS0659: Warning 1 'Test.ClassOTO' overrides Object.Equals(object o) but does not override Object.GetHashCode() D:PersonalblogOverriding the ObjectClassOTOClassOTOClass1.cs 6 18 ClassOTO

which is due to the MS recommendations that the two equal object should have the sam hash code values, so the developer is advised to do that.

Luckily it is very trivial to be implemented:

public override int GetHashCode()

{

return this.ToString().GetHashCode();

}

Again we are reusing the ToString implementation which gives the same string for the same instances and therefore the same hashcodes

NOTE: I'm also implementing the suppression of the general equals method form by implementing the strongly typed version and suppressing the usage of the general one through the attributes. I believe that is a good practice in designing the classes to override the boxed forms of the method with the strongly typed forms.

Something like this:

[Obsolete("Use TypeEquals instead.")]

[EditorBrowsable(EditorBrowsableState.Never)]

public override bool Equals(object obj)

{

return TypeEquals(obj as ClassOTO);

}

 

public bool TypeEquals(ClassOTO typedObj)

{

if (typedObj == null) return false;

       return ToString() == typedObj.ToString();

}

 

This technique allows implementation of the general and typed without the doubling of the code for that. Than we are depreciating usage of the non typed form of the method.

This can be seen in intelisense window

 

and NANT is also showing it as a warning

 

Collection design

If we would have a simple collection of the previously defined objects, like the next one:

namespace Test

{

    public class CollectionOTO

    {

        private ClassOTO[] _collection;

       

        public ClassOTO[] Collection

        {

            get

            {

                return _collection;

            }

            set

            {

                _collection = value;

            }

        }

    }

}

we could continue applying the same design principles on it.

GetHashCode()

we could implement the hash code as a summarized hashcode of the collection items. Something like:

public override int GetHashCode()

{

int result=0;

       foreach (ClassOTO oto in _collection)

       {

              result = result ^ oto.GetHashCode();

}

       return result;

}

(collection hashcode == xor of collection member hashcodes)

Equals(object)

could look something like this:

[Obsolete("Use TypeEquals instead.")]

[EditorBrowsable(EditorBrowsableState.Never)]

public override bool Equals(object obj)

{

return TypeEquals(obj as CollectionOTO);

}

 

public bool TypeEquals(CollectionOTO typedObj)

{

if (typedObj == null) return false;

       return GetHashCode() == typedObj.GetHashCode();

}

NOTE: On collection level I have just used the hashcode instead of the string because:

  • they are two faces of the same value (hashcode is calculated on the string)
  • calculation of collection hash code is much quicker
  • I didn't implement the override for the collection ToString()

ToString()

In case you don't expect that the number of collection members would be big and you would like quickly to see the collection member data you could override ToString() collection method like this:

public override string ToString()

{

int capacity = 0;

       foreach (ClassOTO oto in _collection)

       {

              capacity+=oto.ToString().Length;

}

 

StringBuilder sb=new StringBuilder(capacity);

       for (int i = 0; i < _collection.Length; i++)

       {

       sb.AppendFormat

("Collection item:{0}, value:{1} n"

, i, _collection[i]);           

}

       return sb.ToString();

}

 

So if there would be next console application:

using System;

using Test;

 

namespace ConsoleApplication1

{

    internal class Program

    {

        private static void Main(string[] args)

        {

            ClassOTO[] test = new ClassOTO[5];

 

            for (int i = 0; i < 5; i++)

            {

                ClassOTO temp = new ClassOTO();

                temp.PropertyA = i;

                temp.PropertyB = "Field:" + i;

                temp.PropertyC = DateTime.Now;

 

                test[i] = temp;

            }

 

            CollectionOTO collOTO = new CollectionOTO();

            collOTO.Collection = test;

 

            Console.ReadKey();

        }

    }

}

 

the console output would look like:

Console output

and event the most important benefit of the overriding the ToString method could be sen if we would put a break point on the Console.ReadKeyLine().

The simple ? collOTO command in the immediate window would return all the data about the collection

Immediate window

NOTE: In cases when we don't know the number of collection items (more realistic case) the output of the ToString() method could be very long and his building could take a lot of processing time.

ToString() - Realistic approach

Would also include the iterative enlisting of the collection member data, but this time only the identity attribute (primary key) of the collection item.

Something like this:

public override string ToString()

{

StringBuilder sb = new StringBuilder();

       for (int i = 0; i < _collection.Length; i++)

       {

              sb.AppendFormat

                     ("Collection item:{0}, PK={1} n"

              , i, _collection[i].PropertyA);

       }

       return sb.ToString();

}

The console output would be now:

Console of real case scenario ToString 

And the developer could still do in the immediate window in case he need data of the item with the PK=3 something like this:

Immediate windows in real case usage

And that is still really cool data to get and without the worries on the total number of collection members impact on collection ToString resulting value

 

So, that was first of my posts in Code Better post series... Hope you have enjoy it :)

 

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

No trackbacks yet.