Behind the Scenes Look at C#: Type Conversions continued

We will continue our discussion of type conversion. In this article we will look at implicit and explicit type conversion with reference-types; after that, we will discuss user-defined type conversion and see how we can define our own conversion methods using the keywords implicit or explicit along with the operator keyword. But before we do all that a look at the System.Convert class is in order.

Contributed by
Rating: 5 stars5 stars5 stars5 stars5 stars / 16
August 03, 2005
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

The System.Convert Class

The System.Convert class contains methods to convert from any primitive data type to any other primitive data type (except for the object data type because that's a general data type). The class is declared using the keyword sealed, which means that you can't inherit it. All of the class methods are declared as static, so you access the class members using the class name.

The methods of the System.Convert class are implemented to preserve the culture and to check the ranges (they use the checked block). The methods can throw runtime exceptions like System.InvalidCastException if the conversion operation can't produce a valid value and System.OverflowException when the value exceeds the maximum range of the type that we are converting to, as in the following Main method:

static void Main(string[] args)
{
  short x = 300;
  byte y = Convert.ToByte(x);
  Console.WriteLine(y);
  Console.ReadLine();
}

The code throws a runtime exception of type System.OverflowException because the value 300 is greater than the maximum range of the byte data type. The convert class contains methods that have overloaded versions to accept all of the primitive data types (except object) as a parameter and return a specific data type. For example, the method Convert.ToInt32 has 19 overloaded versions that accept all of the primitive data types and return an Int32 data type. When you use the methods of the convert class to convert from a numeric data type to another data type, the methods will not throw an exception if there's a loss of little precision digits, but they will throw an exception if the value (after the lose of the little precision digits) is greater than the maximum range of the return type.

There is another issue you need to know about before we leave this section. Each primitive data type provides a method called Parse(). Parse() accepts a string representation as a parameter, and returns a parsed value of the string that represents the data type that contains the Parse() method. Of course the Parse() method can throw runtime exceptions, and the exceptions differ from one type to another.

For example, the first line of code uses the Parse() method of the Byte data type, which throws a runtime exception of type System.OverflowException when trying to convert the value 500 to byte data type. The next line of code uses the Parse() method of the DateTime data type to convert the string "Hi Guys" to a DateTime value; it throws a runtime exception of type System.FormatException because the string can't be parsed to represent a DateTime value.

static void Main(string[] args)
{
  byte x = Byte.Parse("500");
  DateTime y = DateTime.Parse("Hi Guys");
}

To use the parse method you need to be sure that the string value will return the right format for that type when it is parsed. Take a look at the following application:

using System;
namespace TypeConversion
{
  class Class1
  {
    static void Main(string[] args)
    {
      string x = "4000";
      int y = Int32.Parse(x) + 1;
      Console.WriteLine(y); 
      Console.ReadLine();
    }
  }
}

When you run this application you will get the value 4001 because the Int32.Parse() method has parsed the string value "4000" and converted it to an Int32 data type.

Implicit and Explicit Reference-Types Conversion

In this section we will discuss the implicit and explicit reference-type conversions in the same class inheritance hierarchy. We are not talking about implicit and explicit user defined type conversions between different classes in different class inheritance hierarchies. The C# compiler knows what to do when you perform explicit type conversion with classes in the same inheritance hierarchy (as we have seen in the previous article in the section "Introduction to Type Conversion, The Reference-Type Example"). The C# compiler also knows what to do when it needs to perform implicit type conversion with classes in the same inheritance hierarchy -- but it doesn't know how to perform implicit and explicit type conversions with classes in different class inheritance hierarchies. That's why C# features a syntax to do this job, as we will see later in this article. For now we will discuss implicit conversions in the same inheritance hierarchy.

Although implicit and explicit type conversions in the same inheritance hierarchy are related to the concepts of inheritance, I think that this is the best place to discuss it, because it's more related to casting and how object references are related to objects in the runtime.

If you remember the example of the section "Introduction to Type Conversion, The Reference-Type Example," you will recall that we had an object of type Worker (called worker1) and we cast the reference worker1 to a reference of type Person. In the next application you will understand the whole implicit and explicit reference-type conversion story. Let's take a look at the code first:

using System;
namespace TypeConversion
{
  class Class1
  {

    static void Main(string[] args)
    { 
      Worker worker1 = new Worker(); 
      worker1.FirstName = "Michael";
      worker1.LastName = "Youssef";
      worker1.Department = "IT";
      Console.WriteLine(worker1);
      Console.WriteLine();
      Person aPerson = worker1; // this doesn't require a cast
      Console.WriteLine("this is aPerson = {0}", aPerson);
      Worker worker2 = (Worker)aPerson; // this requires a cast operation 
      Console.WriteLine("this is worker2 = {0}", worker2);
      Console.ReadLine();
    }
  }

  class Person
  {
    private string firstName;
    private string lastName;

    public string FirstName
    {
      get
      { 
        return this.firstName;
      }

      set
      {
        this.firstName = value;
      } 
    }

    public string LastName
    {
      get
      {
        return this.lastName;
      }

      set
      {
        this.lastName = value;
      } 
    }

    public override string ToString()
    {
      return this.firstName +" "+ this.lastName;
    } 

  }

  class Worker : Person
  {
    private string department;

    public string Department
    {
      get
      {
        return this.department; 
      }

      set
      {
        this.department = value;
      }
    }

    public override string ToString()
    {
      return this.FirstName +" "+ this.LastName+","+this.department;
    }
  }
}

Run the application after you compile it and you will get the following result to the console window.

Let's review the implicit and explicit operations involved when converting from one value-type to another. Implicit conversion means that there's no loss of data; the explicit conversion is needed because there might be some loss of data. The case of reference-types is different. As you can see, the object is heap-based. The implicit and explicit cast operations are performed on the object reference itself, and the result of running the application proves that because the same object values are printed to the console window using the object ToString() method.

We begin the Main method with a declaration of an object reference of type Worker and then assign values to the FirstName, LastName and Department properties. Then, we call the ToString method to print these values to the console window. After that we declare an object reference of type Person (aPerson) and assign the worker1 to it, which assigns the object worker1 to the aPerson reference.

Actually I did use the cast operator in the first part of the article, only to illustrate that casting is needed when you assign a value to another type that stores a range of values that is less than the value's type. Now you understand that with the reference type it's all about the reference object itself (unlike the value types, which simply hold the values on the stack).

So the statement Person aPerson = worker1; doesn't need casting, because here there's no lose of data (because it's heap-based). However, we need an explicit cast operation when we use this statement: Worker worker2 = (Worker)aPerson;. Because we know that aPerson refers to an instance of type Worker, we can cast to type Worker with no problems; again, the object is heap-based and we are dealing only with the object reference.

If the object reference aPerson refers to an object of type Person, then the statement Worker worker2 = (Worker)aPerson; will fail. It will generate the exception System.InvalidCastException because a Worker object contains more members than a Person object. We can't cast that way, as we get a exception of type System.OverflowException when we assign a value that exceeds the maximum range for the value type (when runs it a checked block).

So with reference types there are down-cast and up-cast conversion operations on object references. When you cast an object reference of a derived class type to an object reference of a base class type, then it's called up-cast, and as you know it doesn't need explicit conversion. The down-cast operation works the other way; when you cast an object reference of a base class to an object reference of a derived class, the operation is called a down-cast.

User Defined Type Conversions

When the C# compiler needs to perform an implicit conversion on primitive data types, it knows exactly what it needs and the operations it will perform; it also knows what to do with the explicit cast operations. The same thing applies to the classes that exist in the same class inheritance hierarchy; the C# compiler knows what to do when it needs to perform an implicit or an explicit conversion operation. What if we need to convert from a type to another one that doesn't exist in the same class inheritance hierarchy? C# offers a syntax that's similar to operator overloading that can be used when you need to convert (implicitly or explicitly) from one type to another.

The syntax that C# offers for user defined type conversions can be used to state an implicit type conversion between two classes (or structures) or an explicit type conversion. Of course we have to simulate the compiler's behavior. In other words, we use the cast operator when we convert a double variable to an int because we know that we may lose some data. The C# compiler performs an implicit type conversion when the operation is saved, and it will not lose any data.

The same point applies to user defined conversions. When we are sure that the conversion operation from one type to another type is saved, we will use the syntax that defines an implicit conversion. If the operation can cause some data to be lost, however, we will use the syntax of the explicit conversion (for which the cast operator must be used) to state that. The user defined type conversions can be defined and used with classes as well as structures, and the syntax is the same. Note that you can use the C# user defined type conversion with classes in the same class  inheritance hierarchy, but not between a derived type and a base type; you can use it between two classes that participate in the same base class.

For example, you have the classes Customer and Worker, which both inherit from the class Person. In this class  inheritance hierarchy you can't define user defined type conversions between the Customer class and its base class (Person); you also can't perform the operation between the Worker class and its base class (Person) because they are in the same inheritance hierarchy, and the compiler can perform implicit and explicit casting operations. The only valid user defined type conversion here is between the Customer class and the Worker class. Let's see that in an example:

using System;
namespace TypeConversion
{
  class Class1
  {

    static void Main(string[] args)
    { 
      Worker worker1 = new Worker();
      worker1.FirstName = "Michael";
      worker1.LastName = "Youssef";
      worker1.Department = "IT";
      Console.WriteLine(worker1);
      Console.WriteLine();

      Customer customer1 = new Customer();
      customer1.FirstName = "Mary";
      customer1.LastName = "Jerry";
      customer1.OrderID = "123456789";
      Console.WriteLine(customer1);
      Console.WriteLine();

      Console.WriteLine("casting the worker1 object to customer1");
      customer1 = (Customer)worker1;
      Console.WriteLine();
      Console.WriteLine("the object customer1 after the casting");
      Console.WriteLine();
      Console.WriteLine(customer1);
      Console.ReadLine(); 
    } 
  }

  class Person
  {
    private string firstName;
    private string lastName;

    public string FirstName
    {
      get
      {
        return this.firstName;
      }

      set
      {
        this.firstName = value;
      }
    }

    public string LastName
    { 
      get
      {
        return this.lastName; 
      }

      set
      {
        this.lastName = value;
      } 
    }
  }

  class Worker: Person
  {
    private string department;

    public string Department
    {
      get
      {
        return this.department;
      }

      set
      {
        this.department = value;
      }
    }

    public override string ToString()
    {
      return this.FirstName +" "+ this.LastName+","+this.department;
    }

    public static explicit operator Customer(Worker work)
    {
      Customer temp = new Customer();
      temp.FirstName = work.FirstName;
      temp.LastName = work.LastName;
      temp.OrderID = "000000000";
      return temp;
    }
  }

  class Customer: Person
  {
    private string orderID;

    public string OrderID
    {
      get
      {
        return this.orderID;
      }

      set
      {
        this.orderID = value;
      }
    }

    public override string ToString()
    {
      return this.FirstName +" "+ this.LastName+","+this.orderID;
    }
  }
}

Compile the code and run the application and you will get the following result to the console window:

Explanation and One More Example

We have defined three classes in the hierarchy. The Person base class defines two private fields and two public properties. The Customer and the Worker classes both inherit the Person class, override the ToString() method and add one more property. The Worker class adds the property Department (along with the private field) and the Customers class adds the property OrderID along with the private field orderID. The Worker class defines the explicit casting method.

public static explicit operator Customer(Worker work)
{
  Customer temp = new Customer();
  temp.FirstName = work.FirstName;
  temp.LastName = work.LastName;
  temp.OrderID = "000000000";
  return temp;
}

As you can see, the syntax is very similar to the operator overloading methods; it's public and static, but the new keyword here is the explicit keyword, which states that this is the method that defines the behavior of the explicit conversion that we need to perform using the cast operator. For our example we don't need to define another method for the implicit conversion, because when we cast the worker to the customer type we lose the department data. The cast operator is perfect for this scenario, which we state that we need using the explicit keyword. The return type is Customer and the parameter is of type Worker, so we can write a statement like Customer customer = (Customer)worker. Inside the method we create a new object of type Customer, and we assign the FirstName and the LastName values of the Worker parameter to the Customer instance and set the OrderID to "000000000". The method returns the Customer object after that.

You can put the user defined type conversion method into the Worker class as we did, or into the Customer class, but not into both of them, because the compiler will not know which one to call. In the Main method we simply instantiate an object of type Worker and set its properties, and another object of type Customer and set its properties. We use the statement customer1 = (Customer)worker1; to explicitly cast the worker1 object to a Customer data type (into the customer1) and print the customer1 object, but this time we will get the values that the cast operation produces (which has the FirstName and the LastName values of the worker1 object).

You can perform user defined type conversion between your classes and the built-in types as in the following example.

using System;
namespace TypeConversion
{
  class Class1
  {
    static void Main(string[] args)
    {
      Number number = new Number();
      int y = 90;
      number = y; // implicit conversion
      Console.WriteLine(number);

      Number number1 = new Number();
      number1.x = 56.122;

      y = (int)number1; // explicit conversion

      Console.WriteLine(y);
      Console.ReadLine(); 
    }
  }

  class Number
  {
    public double x;

    public override string ToString()
    {
      return Convert.ToString(x);
    }

    public static implicit operator Number(int var)
    {
      Number num = new Number();
      num.x = var;
      return num;
    }

    public static explicit operator int(Number num)
    { 
      checked
      {
        return (int) num.x;
      }
    }
  }
}

The result that you will get to the console window is:

We have used the implicit and explicit type conversions with this example. The class Number simply represents a number;l I have created it as a simple class, but you can put in any other fields and properties you want. The class contains two type conversion methods that perform the implicit and explicit type conversion operations. The implicit method is very easy, it takes as a parameter the int argument and assign it to the x field, and that completes the implicit conversion. The explicit conversion method takes as a parameter the Number instance and returns an int; as you know, the Number instance's x field is of type double, and when it is converted it will lose the digits after the decimal value (as in the example, we got the value 56), which is why we need an explicit cast for this operation.

blog comments powered by Disqus
C# ARTICLES

- Beginning C#
- ASP.NET RedirectPermanent Method using C# an...
- C Programming Language and UNIX Pioneer Pass...
- Using Facebook JavaScript SDK in ASP.NET wit...
- ASP.NET Export to Excel and Word using VB.NE...
- WAV and MP3 Streaming with ASP.Net and C#
- Game Programming using SDL: the File I/O API
- C# and Java Developer Jobs on the Rise
- The Future Evolution of C# and VB.NET
- C# If and Else-if Statements
- How To Use the C# String Replace Method
- 5 Ways to Parse XML in C#
- C# Meets Design Patterns
- Coding a CRC-Generating Algorithm in C
- Cyclic Redundancy Check

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
 
 
 

ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 7 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials