Recall: Numerical Conversions
In terms of intrinsic numerical types (sbyte,int,float,etc.), an explicit conversion is required when you attempt to store a larger value in a smaller container, as this could result in a loss of data.
int a = 123;
long b = a; // Implicit conversion from int to long
int c = (int)b; // Explicit conversion from long to intRecall: Conversions Among Related Class Types
The C# conversion process allows you to cast up and down the class hierarchy.
// 2 related class type
class Base { }
class Derived : Base { }
// Implicit cast between derived to base.
Base myBaseType;
myBaseType = new Derived();
// Must explicitly cast to store base reference in derived type.
Derived myDerivedType = (Derived)myBaseType;Creating Custom Conversion Routines
C# provides 2 keywords, explicit and implicit, that you can use to control how your types respond during an attempted conversion.
public struct Rectangle
{
public int Width { get; set; }
public int Height { get; set; }
public Rectangle(int w,int h)
{
Width = w;
Height = h;
}
public void Draw()
{
for (int i = 0; i < Height; i++)
{
for (int j = 0; j < Width; j++)
{
Console.Write("*");
}
Console.WriteLine();
}
}
public override string ToString()
{
return $"{Width} {Height}";
}
}
public struct Square
{
public int Length { get; set; }
public Square(int i) : this()
{
Length = i;
}
public void Draw()
{
for (int i = 0; i < Length; i++)
{
for (int j = 0; j < Length; j++)
{
Console.Write("*");
}
Console.WriteLine();
}
}
public override string ToString()
{
return $"{Length}";
}
// Must be defined static
public static explicit operator Square(Rectangle r)
{
Square s = new Square { Length = r.Height };
return s;
}
}
Rectangle r = new Rectangle(15, 4);
Console.WriteLine(r);
r.Draw();
// Conver into a Square, based on the height of the Rectangle.
Square s = (Square)r;
Console.WriteLine(s);
s.Draw();While it may not be all that helpful to convert a Rectangle into a Square within the same scope, assume you have a function that has been designed to take Square parameters.
// This method requires a Square type
static void DrawSquare(Square sq)
{
Console.WriteLine(sq.ToString());
sq.Draw();
}Using your explicit conversion operation on the Square type, you can now pass in Rectangle types for processing using an explicit case, like so:
// Convert Rectangle to Square to invoke method.
Rectangle rect = new Rectangle(10, 5);
Square.DrawSquare((Square)rect);Additional Explicit Conversions for the Square Type
Given that a square is symmetrical on all sides, it might be helpful to provide an explicit conversion routine that allows the caller to cast from an integer type into a Square (which, of course, will have a side length equal to the incoming integer).
public struct Square
{
...
public static explicit operator Square(int sideLength)
{
Square newSq = new Square { Length = sideLength };
return newSq;
}
public static explicit operator int (Square sq)
{
return sq.Length;
}
}
// Converting an into to a Square.
Square sq2 = (Square)80;
Console.WriteLine(sq2);
// Converting a Square to an int
int side = (int)sq2;
Console.WriteLine(side);To be honest, converting from a Square into an integer may not be the most intuitive operation. However, it does point out an important fact regarding custom conversion routines: the compiler does not care what you convert to or from, if you have written syntactically correct code.
Defining Implicit Conversion Routines
Square s3 = new Square { Length = 85 };
// Attempt to make an implicit cast?
Rectangle rect2 = s3;This code will not compile, given that you have not provided an implicit conversion routing for the Rectangle type. Now here is the catch: it is illegal to define explicit and implicit conversion functions on the same type if they do not differ by their return type or parameter set. This might seem like a limitation; however, the second catch is that when a type defines an implicit conversion routine, it is legal for the caller to make use of the explicit cast syntax.
public static implicit operator Rectangle(Square s)
{
Rectangle r = new Rectangle
{
Height = s.Length,
Width = s.Length * 2 // Assume the Length of the new Rectangle with (Length*2)
};
return r;
}
// Implicit cast OK!
Square s3 = new Square { Length = 85 };
Rectangle rect2 = s3;
// Explicit syntax still OK!
Square s4 = new Square { Length = 3 };
Rectangle r3 = (Rectangle)s4;
Console.WriteLine(r3);