#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
Advertisements

#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));
            }
        }