C# Basic (8)

1. Operator overloading

public static retval operator op ( object1 [, object2 ])

Keep in mind the following facts when using operator overloading:

  • All overloaded operator methods must be defined as public and static.
  • Technically, retval (the return value) can be any type. However, it’s common practice to return the type for which the method is being defined with the exception of the true and false operators, which should always return a Boolean value.
  • The number of arguments passed (object1, object2) depends on the type of operator being overloaded. If a unary operator (an operator having a single operand) is being overloaded, there will be one argument. If a binary operator (an operator taking two operands) is being overloaded, two arguments are passed.
  • In the case of a unary operator, the argument to the method must be the same type as that of the enclosing class or struct. In other words, if you redefine the ! unary operator for a class called Foo, that method must take as its only argument a variable of type Foo.
  • If the operator being overloaded is a binary operator, the first argument must be the same type as that of the enclosing class and the second argument can be any type.

Eg: public static Invoice operator+ (Invoice invoice1, Invoice invoice2)

I used the += operator with an Invoice class. You can’t actually overload these compound operators. You can overload only the “base” operator—in this case, the +.

 

2. Only the following unary and binary operators can be overloaded.

Unary operands: +, -, !, ~, ++, –, true, false

Binary operands: +, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, <=

 

3. User-defined conversions enable you to declare conversions on structures or classes such that the struct or class can be converted to other structures, classes, or basic C# types.

the parentheses used for casting cannot be overloaded and that user-defined conversionsmust be used instead.

Fahrenheit f = 98.6F;
Celsius c = (Celsius)f; // User-defined conversion.

This doesn’t provide any functional benefits over the following syntax, but it is more intuitive to write and easier to read.

Fahrenheit f = 98.6F;
Celsius c = f.ConvertToCelsius();

 

4.  The syntax of the user-defined conversion uses the operator keyword to declare user-defined conversions:

public static implicit operator conv-type-out (conv-type-in operand)

public static explicit operator conv-type-out (conv-type-in operand)

There are only a couple of rules regarding the syntax of defining conversions:

  • Any conversion method for a struct or class—you can define as many as you need-must be static.
  • Conversions must be defined as either implicit or explicit. The implicit keyword means that the cast is not required by the client and will occur automatically. Conversely, using the explicit keyword signifies that the client must explicitly cast the value to the desired type.
  • All conversions either must take (as an argument) the type that the conversion is being defined on or must return that type.
  • As with operator overloading, the operator keyword is used in the conversion method signature but without any appended operator.

Eg:

using System;

class Temperature
{
    public Temperature(float Temp)
    {
        this.temp = Temp;
    }

    protected float temp;
    public float Temp
    {
        get
        {
            return this.temp;
        }
    }
}

class Celsius : Temperature
{
    public Celsius(float Temp)
        : base(Temp) {}

    public static implicit operator Celsius(float Temp)
    {
        return new Celsius(Temp);
    }

    public static implicit operator Celsius(Fahrenheit F)
    {
        return new Celsius(F.Temp);
    }

    public static implicit operator float(Celsius C)
    {
        return((((C.temp - 32) / 9) * 5));
    }
}

class Fahrenheit : Temperature
{
    public Fahrenheit(float Temp)
        : base(Temp) {}

    public static implicit operator Fahrenheit(float Temp)
    {
        return new Fahrenheit(Temp);
    }

    public static implicit operator Fahrenheit(Celsius C)
    {
        return new Fahrenheit(C.Temp);
    }

    public static implicit operator float(Fahrenheit F)

    {
        return((((F.temp * 9) / 5) + 32));
    }
}

class Temp2App
{
    public static void DisplayTemp(Celsius Temp)
    {
        Console.Write("Conversion of {0} {1} to Fahrenheit = ", 
            Temp.ToString(), Temp.Temp);
        Console.WriteLine((Fahrenheit)Temp);
    }

    public static void DisplayTemp(Fahrenheit Temp)
    {
        Console.Write("Conversion of {0} {1} to Celsius = ", 
            Temp.ToString(), Temp.Temp);
        Console.WriteLine((Celsius)Temp);
    }

    public static void Main()
    {
        Fahrenheit f = new Fahrenheit(98.6F);
        DisplayTemp(f);

        Celsius c = new Celsius(0F);
        DisplayTemp(c);
    }
}

 

 

5. Delegates,

refer only to single methods and are defined at run time. Delegates have two main usages in C# programming: callbacks and event handling.

using System;

class DBConnection
{
    public DBConnection(string name)
    {
        this.name = name;
    }

      protected string Name;
      public string name
      {
          get
          {
              return this.Name;
          }
          set
          {
              this.Name = value;
          }
      }
  }

  class DBManager
  {
      static DBConnection[] activeConnections;
      public void AddConnections()
      {
          activeConnections = new DBConnection[5];
          for (int i = 0; i < 5; i++)
          {
              activeConnections[i] = 
new DBConnection("DBConnection " + (i + 1));
          }
      }

      public delegate void EnumConnectionsCallback(DBConnection connection);
      public static void EnumConnections(EnumConnectionsCallback callback)
      {
          foreach (DBConnection connection in activeConnections)
          {
              callback(connection);
          }
      }
  }

  class Delegate1App
  {
      public static void ActiveConnectionsCallback(DBConnection connection)
      {
          Console.WriteLine("Callback method called for " 
                            + connection.name);
      }

      public static void Main()
      {
           DBManager dbMgr = new DBManager();
           dbMgr.AddConnections();

      DBManager.EnumConnectionsCallback myCallback = 
      new DBManager.EnumConnectionsCallback(ActiveConnectionsCallback);

      DBManager.EnumConnections(myCallback);
      }
}

Compiling and executing this application results in the following output:

Callback method called for DBConnection 1
Callback method called for DBConnection 2
Callback method called for DBConnection 3
Callback method called for DBConnection 4
Callback method called for DBConnection 5

 

 

6. Defining Delegates as Static Members

using System;

class DBConnection
{
    public DBConnection(string name)
    {
        this.name = name;
    }

    protected string Name;
    public string name
    {
        get
        {
            return this.Name;
        }

        set
        {
            this.Name = value;
        }
    }
}

class DBManager
{
    static DBConnection[] activeConnections;
    public void AddConnections()
    {
        activeConnections = new DBConnection[5];
        for (int i = 0; i < 5; i++)
        {
            activeConnections[i] = new 
DBConnection("DBConnection " + (i + 1));
        }
    }

    public delegate void EnumConnectionsCallback(DBConnection connection);
    public static void EnumConnections(EnumConnectionsCallback callback)
    {
        foreach (DBConnection connection in activeConnections)
        {
            callback(connection);
        }
    }
}

class Delegate2App
{
    public static DBManager.EnumConnectionsCallback myCallback = 
        new DBManager.EnumConnectionsCallback(ActiveConnectionsCallback);
    public static void ActiveConnectionsCallback(DBConnection connection)
    {
        Console.WriteLine ("Callback method called for " + 
                           connection.name);
    }

    public static void Main()
    {
        DBManager dbMgr = new DBManager();
        dbMgr.AddConnections();

        DBManager.EnumConnections(myCallback);
    }
}

 

 

7. Creating Delegates Only When Needed

using System;

class DBConnection
{
    public DBConnection(string name)
    {
        this.name = name;
    }

    protected string Name;
    public string name
    {
        get
        {
            return this.Name;
        }
        set

        {
            this.Name = value;
        }
    }
}

class DBManager
{
    static DBConnection[] activeConnections;
    public void AddConnections()
    {
        activeConnections = new DBConnection[5];
        for (int i = 0; i < 5; i++)
        {
            activeConnections[i] = new 
                DBConnection("DBConnection " + (i + 1));
        }
    }

    public delegate void EnumConnectionsCallback(DBConnection connection);
    public static void EnumConnections(EnumConnectionsCallback callback)
    {
        foreach (DBConnection connection in activeConnections)
        {
            callback(connection);
        }
    }
}

class Delegate3App
{
    public DBManager.EnumConnectionsCallback myCallback
    {
        get
        {
            return new DBManager.EnumConnectionsCallback
                            (ActiveConnectionsCallback);
        }
    }

    public static void ActiveConnectionsCallback(DBConnection connection)
    {
        Console.WriteLine
            ("Callback method called for " + connection.name);
    }

    public static void Main()
    {
        Delegate3App app = new Delegate3App();

        DBManager dbMgr = new DBManager();
        dbMgr.AddConnections();

        DBManager.EnumConnections(app.myCallback);
    }
}

 

8. Delegates Composition

using System;

class Part
{
    public Part(string sku)

    {
        this.Sku = sku;

        Random r = new Random(DateTime.Now.Millisecond);
        double d = r.NextDouble() * 100;

        this.OnHand = (int)d;
    }

    protected string Sku;
    public string sku
    {
        get
        {
            return this.Sku;
        }
        set
        {
            this.Sku = value;
        }
    }

    protected int OnHand;
    public int onhand
    {
        get
        {
            return this.OnHand;
        }
        set
        {
            this.OnHand = value;
        }
    }
}

class InventoryManager
{
    protected const int MIN_ONHAND = 50;

    public Part[] parts;
    public InventoryManager()
    {
        parts = new Part[5];
        for (int i = 0; i < 5; i++)
        {
            Part part = new Part("Part " + (i + 1));
            parts[i] = part;
            Console.WriteLine
                ("Adding part '{0}' on-hand = {1}", 
                 part.sku, part.onhand);
        }
    }

    public delegate void OutOfStockExceptionMethod(Part part);
    public void ProcessInventory(OutOfStockExceptionMethod exception)
    {
        Console.WriteLine("\nProcessing inventory...");
        foreach (Part part in parts)
        {
            if (part.onhand < MIN_ONHAND)
            {
                Console.WriteLine
                    ("{0} ({1}) is below minimum onhand {2}", 
                    part.sku, part.onhand, MIN_ONHAND);

                exception(part);
            }
        }
    }
}

class CompositeDelegate2App
{
    public static void LogEvent(Part part)
    {
        Console.WriteLine("\tlogging event...");
    }

    public static void EmailPurchasingMgr(Part part)
    {
        Console.WriteLine("\temailing Purchasing manager...");
    }

    public static void EmailStoreMgr(Part part)
    {
        Console.WriteLine("\temailing store manager...");
    }

    public static void Main()
    {
        InventoryManager mgr = new InventoryManager();
        InventoryManager.OutOfStockExceptionMethod[] exceptionMethods 
            = new InventoryManager.OutOfStockExceptionMethod[3];

        exceptionMethods[0] = new
            InventoryManager.OutOfStockExceptionMethod
                (LogEvent);
        exceptionMethods[1] = new
            InventoryManager.OutOfStockExceptionMethod
                (EmailPurchasingMgr);
        exceptionMethods[2] = new
            InventoryManager.OutOfStockExceptionMethod
                (EmailStoreMgr);

        int location = 1;

        InventoryManager.OutOfStockExceptionMethod compositeDelegate;

        if (location == 2) 
        {
            compositeDelegate = 
                exceptionMethods[0] + exceptionMethods[1];
        }
        else
        {
            compositeDelegate = 
                exceptionMethods[0] + exceptionMethods[2];
        }

        mgr.ProcessInventory(compositeDelegate);
    }
}

 

 

9. Defining events with Delegates

Once the event is raised, the runtime takes care of notifying each subscriber that the event has occurred.

The method called as a result of an event being raised is defined by a delegate.

First, the delegate must be defined as taking two arguments. Second, these arguments always represent two objects: the object that raised the event (the publisher) and an event information object. Additionally, this second object must be derived from the .NET Framework’s EventArgs class.

using System;

class InventoryChangeEventArgs : EventArgs
{
    public InventoryChangeEventArgs(string sku, int change)
    {
        this.sku = sku;
        this.change = change;
    }

    string sku;
    public string Sku
    {
        get
        {
            return sku;
        }
    }

    int change;
    public int Change
    {
        get 
        {
            return change;
        }
    }
}

class InventoryManager // Publisher.
{
    public delegate void InventoryChangeEventHandler
        (object source, InventoryChangeEventArgs e);
    public event InventoryChangeEventHandler OnInventoryChangeHandler;

    public void UpdateInventory(string sku, int change)
    {
        if (0 == change)
            return; // No update on null change.

        // Code to update database would go here.

        InventoryChangeEventArgs e = new 
            InventoryChangeEventArgs(sku, change);

        if (OnInventoryChangeHandler != null)
            OnInventoryChangeHandler(this, e);
    }
}

class InventoryWatcher // Subscriber.
{
    public InventoryWatcher(InventoryManager inventoryManager)
    {
        this.inventoryManager = inventoryManager;
        inventoryManager.OnInventoryChangeHandler += new
InventoryManager.InventoryChangeEventHandler(OnInventoryChange);
    }
    void OnInventoryChange(object source, InventoryChangeEventArgs e)
    {
        int change = e.Change;
        Console.WriteLine("Part '{0}' was {1} by {2} units", 
            e.Sku, 
            change > 0 ? "increased" : "decreased",
            Math.Abs(e.Change));
    }
    InventoryManager inventoryManager;
}

class Events1App
{
    public static void Main()
    {
        InventoryManager inventoryManager = 
            new InventoryManager();

        InventoryWatcher inventoryWatch = 
            new InventoryWatcher(inventoryManager);

        inventoryManager.UpdateInventory("111 006 116", -2);
        inventoryManager.UpdateInventory("111 005 383", 5);
    }
}

Let’s look at the first two members of the InventoryManager class:

    public delegate void InventoryChangeEventHandler
                               (object source, InventoryChangeEventArgs e);
    public event InventoryChangeEventHandler OnInventoryChangeHandler;

The first line of code is a delegate, which by now you know is a definition for a method signature. As mentioned earlier, all delegates that are used in events must be defined as taking two arguments: a publisher object(in this case, source) and an event information object(an EventArgs-derived object). The second line uses the event keyword, a member type with which you specify the delegate and the method (or methods) that will be called when the event is raised.

The last method in the InventoryManager class is the UpdateInventory method, which is called anytime inventory is changed. As you can see, this method creates an object of type InventoryChangeEventArgs. This object is passed to all subscribers and is used to describe the event that took place.

Now look at the next two lines of code:

        if (OnInventoryChangeHandler != null)
            OnInventoryChangeHandler(this, e);

The conditional if statement checks to see whether the event has any subscribers associated with the OnInventoryChangeHandler method. If it does—in other words, OnInventoryChangeHandler is not null—the event is raised. That’s really all there is on the publisher side of things. Now let’s look at the subscriber code.

The subscriber in this case is the class called InventoryWatcher. All it needs to do is perform two simple tasks. First, it adds itself as a subscriber by instantiating a new delegate of type InventoryManager.InventoryChangeEventHandler and adding that delegate to the InventoryManager.OnInventoryChangeHandler event. Pay special attention to the syntax used—it’s using the += compound assignment operator to add itself to the list of subscribers so as not to erase any previous subscribers.

inventoryManager.OnInventoryChangeHandler 
+= new InventoryManager.InventoryChangeEventHandler(OnInventoryChange);

The only argument that needs to be supplied here is the name of the method that will be called if and when the event is raised.

The only other task the subscriber needs to do is implement its event handler. In this case, the event handler is InventoryWatcher.OnInventoryChange, which prints a message stating the part number and the change in inventory.

Finally, the code that runs this application instantiates InventoryManager and InventoryWatcher classes and, every time the InventoryManager.UpdateInventory method is called, an event is automatically raised that causes the InventoryWatcher.OnInventoryChanged method to be called.

 

Advertisements
This entry was posted in C#. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s