Cast operators allow one object or value type to be assignable to another. Implicit casting is automatically performed by the compiler and should not result in any data or precision loss.
int i = 1;
float f = i;
Explicit casting is performed by the programmer and may result in loss of precision or data.
float f = 1.01;
int i = (int)f;
Objects can be defined in a way that explicit casts are fully reversable.
Let's begin with the following Class and Wrapper:
class ClassToWrap
{
// Class content
}
class WrapperClass
{
private readonly ClassToWrap _wrapped;
// Data not present in the ClassToWrap
public readonly long TimeStamp;
public WrapperClass()
{
_wrapped = new ClassToWrap();
TimeStamp = DateTime.Now.Ticks;
}
private WrapperClass(ClassToWrap x)
{
_wrapped = x;
TimeStamp = DateTime.Now.Ticks;
}
public static explicit operator ClassToWrap(WrapperClass x)
{
return x._wrapped;
}
public static implicit operator WrapperClass(ClassToWrap x)
{
return new WrapperClass(x);
}
}
When the Class is wrapped, it is given a timestamp. Therefore it gains precision with the implicit cast operator. Returning the wrapped class removes that precision, which leads to the explicit cast operator. We can test this with some code:
// Test Code
var ctw = new ClassToWrap();
WrapperClass w;
for (int i = 0; i < 5; i++)
{
w = (WrapperClass)ctw;
System.Console.WriteLine(w.TimeStamp);
Console.ReadKey(); // Or time delay
w = null;
GC.Collect();
}
w = new WrapperClass();
for (int i = 0; i < 5; i++)
{
ctw = (ClassToWrap)w;
System.Console.WriteLine(((WrapperClass)ctw).TimeStamp);
Console.ReadKey(); // Or time delay
ctw = null;
GC.Collect();
}
Running the code can produce results similar to the following:
606728648271881249 606728648280352310 606728648282462570 606728648284399408 606728648286475289 606728648290147555 606728648293511099 606728648297668788 606728648310947587 606728648346554067
Clearly a new WrapperClass is created with each loop. We can fix this by modifying the implicit cast operator.
private static System.Runtime.CompilerServices.ConditionalWeakTable _maps
= new System.Runtime.CompilerServices.ConditionalWeakTable();
public WrapperClass()
{
_wrapped = new ClassToWrap();
TimeStamp = DateTime.Now.Ticks;
_maps.Add(_wrapped,this);
}
private WrapperClass(ClassToWrap x)
{
_wrapped = x;
TimeStamp = DateTime.Now.Ticks;
_maps.Add(_wrapped,this);
}
public static explicit operator ClassToWrap(WrapperClass x)
{
return x._wrapped;
}
public static implicit operator WrapperClass(ClassToWrap x)
{
if (x == null) { return null; }
WrapperClass wc;
if (!_maps.TryGetValue(x, out wc) || wc == null)
{
wc = new WrapperClass(x);
}
return wc;
}
Now when we run the test code, we produce results similar to the following:
606728649244308812 606728649244308812 606728649244308812 606728649244308812 606728649244308812 606728649259649150 606728649259649150 606728649259649150 606728649259649150 606728649259649150
Two-way casting appears to be working with no loss of precision. It is probably safe to turn the explicit cast operator to an implicit cast operator.
public static implicit operator ClassToWrap(WrapperClass x)
{
return x._wrapped;
}