Conversion Operators

Conversion Operators

Implementing conversion operators facilitates the implicit or explicit casting of user-defined types to built-in types or even other user-defined types. Developers implement a conversion operator to explain to the compiler how to interpret a user-defined type in the context of another type. Like mathematical and logical operators, the primary reason for implementing conversion operators is convenience. It is never required. You could as easily expose ToType methods, such as ToInt, ToFloat, or ToDecimal.

An implicit cast is considered a secure cast, whereas explicit casting is required for casting that is not secure. For built-in types, a secure cast is available when there is no potential loss of precision or accuracy. When there is potential loss of precision or accuracy, an explicit cast is required. For example, an int can be assigned to long implicitly. Eight bytes are reserved for a long value and four bytes are reserved for an int value. The int value will be promoted to a long. The promotion occurs silently—no warning or notice. The reverse, in which a long is assigned to an int, requires an explicit cast. This is exhibited in the following code:

int a=5;
long b=10;
b=a;        // implicit cast
a=(int) b;  // explicit cast

C# does not support conversion constructors. A conversion constructor is a one-argument constructor used to create an instance of a type from a different type. Conversion constructors are supported in C++ but are not allowed in C#. Conversion constructors were convenient—too convenient. Conversion constructors were sometimes called transparently when a compiler error for mismatched types was more appropriate.

You cannot overload the cast operator directly. Instead, overload the cast operator selectively with conversion operator methods. This is the syntax of a conversion operator:

  • public static implicit operator returntype(classtype obj)

  • public static explicit operator returntype(type obj)

For the conversion operator syntax, there are many similarities when compared with mathematical and relational operators. Conversion operators must be public and static. Other modifiers, such as virtual and sealed, are syntax errors. Conversion operators that are implicit do not require casting, whereas explicit conversion operators require casting for use. I recommend explicit casting in all circumstances. Implicit casting allows the conversion function to be called transparently and sometimes inadvertently, which may cause undetected side effects. With explicit casting, developers affirmatively state their intentions through casting. Either the return or operand of the conversion operator must be the same as the containing type. If converting to the containing type, the return type should be the containing class. Notice that the return type is after the operator keyword, not before. When converting from the containing type, the operand should be the same type as the containing class.

Here is sample code of implicit and explicit conversion methods. The ZClass has two conversion operators. The first conversion operator converts ZClass to an int. The second conversion operator converts a YClass to a ZClass.

using System;

namespace Donis.CSharpBook{
    public class Starter{
        public static void Main(){
            ZClass obj1=new ZClass(5,10);
            int ival=obj1;
            YClass obj2=new YClass(5);
            // ZClass obj3=obj2; [ error ]
            ZClass obj3=(ZClass) obj2;
        }
    }

    public class ZClass {

        public ZClass(int _fielda, int _fieldb) {
            fielda=_fielda;
            fieldb=_fieldb;
        }

        public static implicit operator int(ZClass curr) {
            return curr.fielda+curr.fieldb;
        }

        public static explicit operator ZClass(YClass curr) {
            return new ZClass(curr.field/2, curr.field/2);
        }

        public int fielda, fieldb;
    }

    public class YClass {

        public YClass(int _field) {
            propField=_field;
        }

        private int propField;
        public int field {
            get {
                return propField;
            }
            set {
                propField=value;
            }
        }
    }
}

Conversion operators are often overloaded to provide the illusion of a user-defined type, which can support a variety of casts. In the following code, the conversion operator is overloaded several times to allow the conversion of ZClass instances to a variety of types:

using System;

namespace Donis.CSharpBook{

    public class ZClass {

        public ZClass(int _fielda, int _fieldb) {
            fielda=_fielda;
            fieldb=_fieldb;
        }

        public static explicit operator int(ZClass curr) {
            return curr.fielda+curr.fieldb;
        }

        public static explicit operator float(ZClass curr) {
            return (float) (curr.fielda+curr.fieldb);

        }

        public static explicit operator short(ZClass curr) {
            return (short) (curr.fielda+curr.fieldb);
        }

        // and so on

        public int fielda, fieldb;
    }
}

Operator String

The operator string operator is a special conversion operator that converts a user-defined type to a string. This appears to overlap with the ToString method, which is inherited from System.Object. Actually, every type is also automatically provided an operator string method, which simply calls the polymorphic ToString method. Look at the following code. If a class has both a ToString and operator string method, which method is called in the Console.WriteLine?

using System;

namespace Donis.CSharpBook{
    public class Starter{
        public static void Main(){
            ZClass obj=new ZClass();
            Console.WriteLine(obj);
        }
    }

    public class ZClass {

        public static implicit operator string(ZClass curr) {
            return "Zlass.operator string";
        }

        public override string ToString() {
            return "ZClass.ToString";
        }

    }
}

The preceding program displays ZClass.operator string. The operator string is called for the Console.WriteLine operand. Why? Unlike the default operator string, the custom operator string does not call ToString. In most circumstances, calling ToString in the operator string is the best practice, which eliminates the necessity of implementing a custom operator string. The default operator string already has this behavior. You simply override the ToString method with the proper string representation of the type. Inconsistencies and confusion can occur when the operator string and ToString have disparate implementations.