#401 – Every Object Inherits an Equals Method

Every type inherits directly or indirectly from System.Object, so each has an Equals method.  This includes both value types (which inherit from System.ValueType) and reference types (which inherit from System.Object).

            public virtual bool Equals(Object object);

The Equals method returns true if the object passed in to the method is equal to the object on which the method was called.

System.Object includes a default implementation of the Equals method that :

  • For value types, returns true if the underlying values are the same (even though there are two copies of the value)
  • For reference types, returns true if the references refer to the exact same object in memory
            float f1 = 10.2f;
            float f2 = 10.2f;
            bool sameFloat = f1.Equals(f2);     // true - same value

            Dog kirby = new Dog("Kirby", 13);
            Dog kirby2 = new Dog("Kirby", 13);
            bool sameDog = kirby.Equals(kirby2);   // false - two different objects

            Dog kirby3 = kirby;
            sameDog = kirby3.Equals(kirby);    // true - two references to same object
Advertisement

#400 – Overloading Binary Operators

You can overload any of the following binary operators: +, -, *, /, %, &, |, ^, <<, >>.  A binary operator is an operator applied to two operands.

For example, assume we have a Dog class that has the following bool properties: BarksALot, LikesBalls and Sheds.  We might implement the & operator for the Dog class so that we get a new Dog instance with all of the boolean properties AND’d together.

        public static Dog operator &(Dog d1, Dog d2)
        {
            Dog newDog = new Dog(string.Format("{0} & {1}", d1.Name, d2.Name), 0);

            newDog.BarksALot = d1.BarksALot & d2.BarksALot;
            newDog.LikesBalls = d1.LikesBalls & d2.LikesBalls;
            newDog.Sheds = d1.Sheds & d2.Sheds;

            return newDog;
        }

We can now apply this operator to two Dog instances.

            Dog kirby = new Dog("Kirby", 13);
            kirby.LikesBalls = true;
            kirby.BarksALot = true;

            Dog jack = new Dog("Jack", 10);
            jack.Sheds = true;
            jack.BarksALot = true;

            Dog newguy = kirby & jack;

#399 – Overloading Unary Operators

You can overload any of the following unary operators: +, -, !, ~, ++, –, true, false.  A unary operator is an operator that can be applied to a single operand.

Overloading the negation (!) operator allows us to negate an instance of a class.

            Dog kirby = new Dog("Kirby", 13);

            Dog antiKirby = !kirby;

To overload the operator, we define a new method in our class that takes an instance of a Dog and “negates” it, returning a new instance.

        public static Dog operator !(Dog d1)
        {
            string notName = new string(d1.Name.Reverse().ToArray());

            return new Dog(notName, -1 * d1.Age);
        }

Below is an example of overloading the increment (++) operator.  (We really ought to overload the decrement operator as well).

            Dog olderKirby = kirby++;
        public static Dog operator ++(Dog d1)
        {
            return new Dog(d1.Name, d1.Age++);
        }

#398 – Overloadable Operators

When you define an operator for a class, you are defining the behavior for that operator when acting upon instances of the class.  This is also known as overloading the operator.

You can overload any of the operators listed below.

  • Unary operators  (apply to one operand):  +, -, !, ~, ++, –, true, false
  • Binary operators  (apply to two operands):  +, -, *, /, %, &, |, ^, <<, >>
  • Comparison operators  (apply to two operands): ==, !=, <, >, <=, >=

When you overload the comparison operators, you must overload them in pairs:

  • Implement ==, != together
  • Implement <, > together
  • Implement <=, >= together

#397 – Defining an Operator

You can define an operator in a class, so that the operator can be used in expressions that include instances of the class.

For example, suppose you want to define the plus (+) operator so that it has some meaning when applied to instances of the Dog class.

            Dog k = new Dog("Kirby", 13);
            Dog j = new Dog("Jack", 15);

            Dog mutant = k + j;

To define an operator in your class, you define a new public static method that includes the operator keyword.

In the example below, we define the plus (+) operator for the Dog class, which allows “adding” two dogs.

        public static Dog operator +(Dog d1, Dog d2)
        {
            return new Dog(d1.Name + d2.Name, d1.Age + d2.Age);
        }

This method returns a new instance of a Dog, with the two names appended together and the dogs’ ages added.

#396 – Operators as Class Members

In C#, an operator is a symbol that defines some operation that should take place when evaluating an expression.

For example, the plus sign (+) is an operator that is typically used to add two numbers, or two instances of a class.

            int a = 5 + 6;   // 11
            string s = "Corn" + "dog";   // Corndog

For most operators, if you try using them on instances of a class that you define, the operator will have no meaning and you’ll get a compile-time error.  (With the exception of the equality (==) and inequality (!=) operators, which are automatically defined for every type).

If you want a particular operator to make sense for instances of a class that you define, you can define that operator as a member of your class.  For example, I could define the plus (+) operator for the Dog class, such that it merges the dog’s names and adds their ages.

#395 – Overriding the Default add and remove Accessors for an Event

When you use the event keyword to define an event, the compiler automatically creates a private backing variable that is a delegate instance, as well as code that encapsulates adding and removing methods to the delegate’s invocation list.

        public event EventHandler Barked;


You can also explicitly declare the backing variable and the add/remove accessors.  The following code results in equivalent behavior to the code shown above.

        private EventHandler barked;
        public event EventHandler Barked
        {
            add { barked += value; }
            remove { barked -= value; }
        }

To the client code, this looks the same–we just have a Barked event. But you could now add some custom code, e.g. logging.

        private EventHandler barked;
        public event EventHandler Barked
        {
            add
            {
                barked += value;
                Logger.Log(string.Format("Added handler {0} ({1})", value.Method.Name, value.Method.ReflectedType));
            }

            remove
            {
                barked -= value;
                Logger.Log(string.Format("Removed handler {0} ({1})", value.Method.Name, value.Method.ReflectedType));
            }
        }

#394 – Raising an Event in a Thread-Safe Manner

When you raise an event, you typically check to make sure that the event is not null, i.e. that at least one handler exists.   This method could result in a null reference exception if another thread removes all handlers after we check for null, but before we raise the event.

A more thread-safe approach is to make a copy of the event before raising it.  If a different thread removes all handlers from the original event, the copy is unaffected.

        public event EventHandler Barked;
        protected virtual void OnBarked()
        {
            EventHandler eventCopy = Barked;

            if (eventCopy != null)
                eventCopy(this, null);
        }

A cleaner approach, also thread-safe, is to declare the event with a null handler.  We can then avoid checking for null, since there will always be at least one handler.

        public event EventHandler Barked = delegate { };

        protected virtual void OnBarked()
        {
            Barked(this, null);
        }

#393 – Implement a Helper Method to Raise an Event

You typically raise an event from within a class by first checking to see if the event is null, i.e. if there are any handlers defined for the event.

        public void Bark(string barkSound)
        {
            Console.WriteLine("{0} says {1}", Name, barkSound);

            if (Barked != null)
                Barked(this, new BarkEventArgs(Name, barkSound));
        }

It’s common practice, however, to define a helper method in your class that actually raises the event.  This method encapsulates the logic of raising the event, including the check for null.  If you also define this method as protected virtual, future subclasses of your class can override the helper method to override the behavior of raising the event.

        public void Bark(string barkSound)
        {
            Console.WriteLine("{0} says {1}", Name, barkSound);
            OnBarked(barkSound);
        }

        protected virtual void OnBarked(string barkSound)
        {
            if (Barked != null)
                Barked(this, new BarkEventArgs(Name, barkSound));
        }

#392 – Reversing a String Using the Reverse Method

Starting with .NET 3.5, you can use a number of extension methods that are part of the System.Linq namespace to act upon any type that implements IEnumerable<T>.  This includes the string type in C# (System.String), which implements IEnumerable<char>.

One of the most useful extension methods that comes with Linq is the Reverse method (Enumerable.Reverse<TSource>).  Because this is an extension method, you can use it on an instance of a string as if it were an instance method.

            string funnyMan = "Roscoe Arbuckle";

            string backwardsGuy = new string(funnyMan.Reverse().ToArray());

Because Reverse returns an object of type ReverseIterator<char>, we need to convert it back to an array and then use the array to instantiate a new string.