No Custom Conversions for Interfaces in C#

September 14th 2018 C#

There's a lot of flexibility in type conversions in C#. On top of built-in implicit and explicit conversions, you can also define custom conversions. However, you might not be aware that these are not supported for interfaces.

As the name implies, implicit type conversions can be used without any special syntax:

var derivedInstance = new DerivedClass();
BaseClass convertedToBase = derivedInstance;
IInterface convertedToInterface = derivedInstance;

They only work when no data will be lost and there's no danger of run-time exceptions. Typical use cases are conversions from derived classes to their base classes and to interfaces they implement. They are also used for converting numeric data types to their larger counterparts, e.g. for converting an int to a long.

When these conditions are not fulfilled, an explicit type conversion must be used:

BaseClass baseInstance = new DerivedClass();
DerivedClass convertedToDerived = (DerivedClass)baseInstance;

Explicit type conversions can throw exceptions at run time, e.g. when you try to convert from a base type to a derived type which does not match the value stored in the variable. There's still some compile time checking, though. The code will not compile if you try to convert between two unrelated types.

To enable such conversions you can implement your own custom implicit or explicit conversions for converting other types to your type:

public static implicit operator ImplicitConversionClass(DerivedClass instance)
{
    return new ImplicitConversionClass();
}

public static explicit operator ExplicitConversionClass(DerivedClass derived)
{
    return new ExplicitConversionClass();
}

The syntax for invoking them is the same as for built-in type conversions:

var instance = new DerivedClass();
ImplicitConversionClass convertedImplicitly = instance;
ExplicitConversionClass convertedExplicitly = (ExplicitConversionClass)instance;

However, this will only work for conversions from other types. The compiler will not allow you to define a conversion from an interface:

public static implicit operator ImplicitConversionClass(IInterface instance)
{
    return new ImplicitConversionClass();
}

The above code will fail to compile with the following error:

User-defined conversions to or from an interface are not allowed

Even if you try to implement a conversion for an interface dynamically, using DynamicObject, it still won't work:

public override bool TryConvert(ConvertBinder binder, out object result)
{
    if (binder.Type.Equals(typeof(IInterface)))
    {
        result = new DerivedClass();
        return true;
    }
    else
    {
        result = null;
        return false;
    }
}

The code will compile, of course. And so will the following dynamic code, trying to use this conversion:

dynamic instance = new CustomDynamicObject();
var convertedToInterface = (IInterface)instance;

An exception will be thrown at runtime, though:

System.InvalidCastException: Unable to cast object of type 'InterfaceCustomConversions.CustomDynamicObject' to type 'InterfaceCustomConversions.IInterface'.

If you try to debug the code, you'll notice that the TryConvert method will not even get called. It's because custom conversions are not allowed for interfaces. Therefore, the dynamic binder only checks if the value in instance implements the interface. Once it establishes that, it immediately throws the exception without any further checks if there are any custom conversions available.

Thanks to the limitation of no custom conversions for interfaces, you can safely assume that any type conversions to interfaces will always leave the converted value intact: no new object will be created and no loss of information will happen.

Get notified when a new blog post is published (usually every Friday):

If you're looking for online one-on-one mentorship on a related topic, you can find me on Codementor.
If you need a team of experienced software engineers to help you with a project, contact us at Razum.
Copyright
Creative Commons License