Controlling Type conversion in C#

Ever run into a sit­u­a­tion where your appli­ca­tion needs to use a type for its inter­nal work­ing but occa­sion­al­ly need to con­vert that type into anoth­er just so it can be passed to anoth­er appli­ca­tion which doesn’t under­stand some of the base types we have in the .Net space?

Con­sid­er the exam­ple below, where the Play­er is a type in my prob­lem domain, but I need to com­mu­ni­cate infor­ma­tion about the play­er with a non-.Net client which doesn’t have the Guid type, so I need to have a Play­erD­TO type used exclu­sive­ly for mes­sage pass­ing:

public sealed class Player
{
    public Player(string name, long score)
    {
        Name = name;
        Score = score;
        ID = Guid.NewGuid();
    }

    public string Name { get; private set; }
    public Guid ID { get; private set; }
    public long Score { get; private set; }
}

public sealed class PlayerDTO
{
    public PlayerDTO(string name, long score, string id)
    {
        Name = name;
        Score = score;
        ID = id;
    }

    public string Name { get; private set; }
    public string ID { get; private set; } // client is not .Net based so no Guid there
    public long Score { get; private set; }
}

The prob­lem here is that there is no easy way to con­vert the Play­er type to Play­erD­TO and every time I want to cre­ate a new Play­erD­TO object I need to man­u­al­ly copy the val­ues from Play­er into PlayerDTO’s con­struc­tor, and my appli­ca­tion needs to know that it needs to con­vert Player.PlayerID into a string.

A clean­er solu­tion here is to over­load either the implic­it or explic­it con­vert­er so you can sim­ply cast a Play­er object into a Play­erD­TO object:

// Overload explicit cast converter to allow easy conversion from a Winner to WinnerAmfvo object
public static explicit operator PlayerDTO(Player player)
{
    return new PlayerDTO(player.Name, player.Score, player.PlayerID.ToString());
}

Fur­ther more, this allows you to eas­i­ly con­vert an array of Play­er objects into an array of Play­erD­TO objects:

// use Array.ConvertAll
PlayerDTO[] playerDTOs = Array.ConvertAll(players, p => (PlayerDTO) p);
// use Linq
playerDTOs = players.Select(p => (PlayerDTO) p).ToArray();

No Interface types allowed

One thing to bear in mind when using the implicit/explicit oper­a­tors is that you won’t be able to use inter­face types, and the rea­son is spec­i­fied in Sec­tion 10.9.3 of the C# spec.

In short, defin­ing an implic­it or explic­it con­ver­sion between ref­er­ence types gives the user the expec­ta­tion that there will be a change in ref­er­ence; after all, the same ref­er­ence can­not be both types. On the oth­er hand, the user does not have the same expec­ta­tion for con­ver­sions between ref­er­ence types and inter­face types.

In these cas­es, you still have oth­er ways to make type con­ver­sion clean­er and cen­tralised, for exam­ple:

Cre­ate a FromI­Play­er method


playerDTOs = iPlayers.Select(p => PlayerDTO.FromPlayer(p)).ToArray();

Put all con­ver­sions in a Play­er­Con­vert sta­t­ic object

If you have lots of types which can be con­vert­ed to many types then it might be a clean­er solu­tion to just put all the con­ver­sions into one sta­t­ic object, like the System.Convert class. You then use it like this:


playerDTOs = iPlayers.Select(p => PlayerConvert.ToPlayerDTO(p)).ToArray();

References:

StackOverflow question on why you can't use interface types with implicit/explicit operator