A binary operator declaration must take two parameters one of which must be the type of the containing class. A binary operator can return any type. Certain binary operators require pair-wise declaration (if one is defined, then the other must be defined as well:
• operator== and operator!= • operator > and operator < • operator >= and operator <=
3. Conversion operators
A conversion operator is used to convert from a source type S to a target type T. The source type S is indicated by the parameter type of the operator declaration and the target type T is indicated by the return type of the operator declaration. A class or a struct is allows to declare a
conversion operator if all the following are true: • S and T are different types.
• Either S or T is the class or struct type in which the conversion operator is declared. • Neither S nor T is object or an interface-type.
• T is not a base-class of S, and S is not a base-class of T. A conversion operator declaration introduces a user-define conversion.
• A conversion operator declaration that includes implicit introduces a user-defined implicit conversion
Implicit conversion occur in a variety of situation including function member invocation, cast expressions and assignments. Implicit conversion occur implicitly without having to specify a cast.
• A conversion operator declaration that includes explicit introduces a user-defined explicit conversion
Explicit conversion must occur in cast expressions, else a compile-time error is
generated. Again, only use explicit conversion if the conversion can cause information to be lost or can throw an exception.
Note that by eliminating unnecessary casts, implicit conversion can improve source code readability. However, because implicit conversions can occur without the programmer specifying them, care must be taken to prevent unpleasant surprises in the form of exception. Therefore, implicit conversions should never throw exceptions and should never lose information so that they can be used safely without the programmer worrying about it. If a conversion operator cannot meet these criteria, then it should be an explicit conversion operator.
Conversion operators are not allowed to redefine a pre-defined conversion. Thus, a conversion operator is not allowed to convert from / to object because implicit and explicit conversion operators already exist between object and all other types.
Conversion operators are also not allowed from or to an interface type. This ensures that a conversion to an interface type succeeds only in object being converted actually implements the interface type.
The signature of a conversion operator consists of the source type and the target type only (this is the only class member for which the return type is part of the signature). This also means that implicit and explicit keywords are not part of the conversion operator signature. The following example illustrates most of the above concepts:
public class Point {
/* Data members */ private int nX, nY; /* Constructors */ public Point() {
nX = nY = 0; }
public Point( int x, int y ) {
nX = x; nY =y; }
/* Unary Operators */
// Unary operators ++ and --: Operator declaration must take and return the
// type of the containing class
public static Point operator++(Point pt) {
// Increment values pt.nX++;
pt.nY++;
// Now return new result return pt;
}
public static Point operator--(Point pt) {
// Decrement values pt.nX--;
pt.nY--;
// Now return new result return pt;
}
// Unary operators true and false: Operator declaration must take the type
// of the containing class and return bool. Note that operator definition
// for true and false must be pair-wise. If one is defined, then the other
// must tbe defined as
public static bool operator true(Point pt) {
// It is up to us to define what operator true means for a Point class.
// In this case, the criteria is that neither nX nor nY must be zero if ((pt.nX > 0) && (pt.nY > 0)) return true; else return false; }
public static bool operator false(Point pt) {
// Delegate to operator true if (pt)
return true; else
}
/* Binary Operators */
// Binary operator + and -: operator declaration must take two parameters of which
// at least one must be the type of the containing class. The declaration can return
// any type
public static Point operator+( Point ptLHS, Point ptRHS ) // LHS: Left-hand-source. RHS:Right-hande-source
{
// Create and initialize a new point Point pt = new Point();
pt.nX = ptLHS.nX + ptRHS.nX; pt.nY = ptLHS.nY + ptRHS.nX; // return results
return pt; }
public static Point operator-( Point ptLHS, Point ptRHS ) // LHS: Left-hand-source. RHS:Right-hande-source
{
// Create and initialize a new point Point pt = new Point();
pt.nX = ptLHS.nX - ptRHS.nX; pt.nY = ptLHS.nY - ptRHS.nX; // return results return pt; } /* Conversion Operators */
// Conversion operator: convert implicitly (no cast required) form PointF to Point
public static implicit operator Point (PointF ptF) {
return new Point( (int)ptF.fX, (int)ptF.fY ); }
// Conversion operator: convert explicitly (cast required) from PointD to Point
public static explicit operator Point (PointD ptD) {
return new Point( (int)ptD.dX, (int)ptD.dY ); }
}
/* These classes are used to illustrate conversion operators for Point class */
public class PointF {
public float fX, fY;
public PointF( float x, float y ) { fX = x; fY = y; } }
public class PointD {
public double dX, dY;
public PointD( double x, double y ) { dX = x; dY = y; } }
// Create a new instance Point pt = new Point(1,2); // Test unary operators
pt++; // Invokes operator++ pt--; // Invokes operator-- if (pt) // Invokes operator true Trace.WriteLine( "pt is true" );
// Test binary operators
Point pt1 = new Point( 10, 10 ); Point pt2 = new Point( 20, 20 );
Point pt3 = pt1 + pt2; // Invokes operator+ // Test conversion operators
PointF ptF = new PointF( 10.0F, 20.0F );
Point pt4 = ptF; // Invokes operator Point (PointF ptF)
PointD ptD = new PointD( 10.0D, 10.0D );
Point pt5 = (Point)ptD; // Invokes operator Point (PointD ptD)
C
ONSTRUCTORS
There are two types of constructors in C#: • Instance Constructors
• Static Constructors
Instance Constructors
An instance constructor is a class member that implements actions required to initialized an instance of a class. Declaring a constructor takes the following forms:
[attributes] [modifiers] class-name (formal-parameter-list) :
base (argument-list) { ... }
[attributes] [modifiers] class-name (formal-parameter-list) :
this (argument-list) { ... }
Where [modifiers] can be: • public
• protected • internal • private • extern
Instance constructors are not inherited. Hence, a class has no instance constructors other than those defined in the class. If a class contains no instance constructor declaration, a default
instructor (one that takes no parameter) is provided automatically.
Constructor Initializers
In the constructor declaration form, base( argument-list) and this(argument-list) are known as constructor initializers. Constructors initializers are used to invoke other instance constructors immediately before the constructor body as follows:
• base( argument-list): causes the appropriate instance constructor of the direct- base-class to be invoked.
• this(argument-list) : causes the appropriate instance constructor from the containing class to be invoked.
• If an instance constructor has no constructor initializers, an instance constructor initializer of the form base() is automatically provided. This ensures that base class constructors are called as appropriate.
Note that constructor initializers are only allowed to access the parameters passed to the instance constructor. The following example illustrates:
public class BaseLevel1 { public BaseLevel1() { Trace.WriteLine("BaseLevel1.BaseLevel1()"); } public BaseLevel1(int n) { Trace.WriteLine("BaseLevel1.BaseLevel1(int n)"); } }
public class DerivedLevel1 : BaseLevel1 {
public DerivedLevel1() // Implicitly calls base()
{
Trace.WriteLine( "DerivedLevel1.DerivedLevel1()"); }
public DerivedLevel1( double d ) // Implicitly calls base()
{
Trace.WriteLine( "DerivedLevel1.DerivedLevel1(double)"); }
public DerivedLevel1( int n ) : base(n) // Calls and passes parameter to base class constructor
{
Trace.WriteLine( "DerivedLevel1.DerivedLevel1(int)"); }
public DerivedLevel1( int n, string s ) : this(n) // Calls and passes parameter to an instance constructor in this class { Trace.WriteLine( "DerivedLevel1.DerivedLevel1(int, string)"); } }
// Test2 constructor initializers
Trace.WriteLine( "Calling constructor with no argument" ); DerivedLevel1 ob1 = new DerivedLevel1();
Trace.WriteLine( "\nCalling constructor with an int argument" ); DerivedLevel1 ob2 = new DerivedLevel1( 10 );
Trace.WriteLine( "\nCalling constructor with an int argument and a string argument" );
In the example above, the this() form of a constructor initializer in commonly used in conjunction with operator overloading to implement optional instance constructor parameters:
/* Optional constructor parameters */ public class Person
{
// Data members private int nAge;
private string strName; // Main constructor
public Person ( int age, string name ) {
nAge = age; strName = name; }
// Overloaded constructors that delegate to the main