Being Cellfish

Stuff I wished I've found in some blog (and sometimes did)

December, 2012

Change of Address
This blog has moved to
  • Being Cellfish

    Type casting with extension methods


    Once in a while I need to convert one object from one type to another because they represent slightly different views of the same data but they do not share a common parent. An example would be an object used internally representing some complex state for something (let us call it FooComplex) and someting simple you just want to return in an API (let us call it FooSimple). So how do I convert between these? There are several options:

    fooSimple.ToComplex() and fooComplex.ToSimple()
    My first option is to att a member function to each of the classes that converts to the other. The first obvious downside with this is that these classes now both need to know about eachother which is probably really bad since they might be in different assemblies. It also means that when I need to add a property in both classes I need to also change the converters in two different files.

    Explicit casts
    You can also implement explicit cast operators which is really just a variant of the option above. Some may argue that it is better since it uses a language feature to illustrate that you convert between two classes. While that is true I think it will be confusing since cast operators are typically used to change the type when the two types actually have an inheritance relationship. So casting between two logically related but in practice seperate types might actually be more confusing than using ToXxx methods.

    Partial classes
    To get around the downside of having ToComplex and ToSimple in two different code files you can always make FooSimple and FooComplex partial classes. But this might not always be possible.

    fooSimple.ToComplex() and FooSimple.FromComplex(FooComplex)
    To get around the problem of two way dependencies and needing to implement the conversion in two different classes (and files) you can just replace one direction with a static FromXxx method. This works pretty well but now you have two different patterns for converting depedning on direction (To vs From).

    Extension methods to the rescue!
    However if you make your ToComplex and ToSimple methods be extension methods I think you get the best of all worlds; Both directions in the same file, same pattern and not using explicit cast operators. I also like to call them AsXxx rather than ToXxx to indicate there is some transformation happening.

  • Being Cellfish

    Task-based Asynchronous Pattern - WhenRandom


    I couldn't resist to create a method to deal with a scenario even less common than WhenSome. The crazy scenario here is that you have N tasks of type Task<T> and you want to return when a random task completes. The easiest way to do this is to just pick a random task and wait for it like this:

     1: public static Task<T> WhenRandom<T>(params Task<T>[] tasks)
     2: {
     3:     var random = new Random();
     4:     return (from task in tasks
     5:             orderby random.Next()
     6:             select task).FirstOrDefault();
     7: }

    However what if you want to be able to cancel the WhenRandom call you need to be a little more creative I guess:

     8: public static async Task<T> WhenRandom<T>(CancellationToken cancellationToken,
     9:                                           params Task<T>[] tasks)
     10: {
     11:     var random = new Random().Next(0, tasks.Length);
     12:     var remainingTasks = new List<Task<T>>(tasks);
     13:     while (remainingTasks.Count > 0)
     14:     {
     15:         await Task.WhenAny(remainingTasks);
     16:         for (int i = 0; i < remainingTasks.Count; i++)
     17:         {
     18:             if (remainingTasks[i].IsCompleted)
     19:             {
     20:                 if (random-- == 0)
     21:                 {
     22:                     return await remainingTasks[i];
     23:                 }
     25:                 remainingTasks.RemoveAt(i--);
     26:             }
     27:         }
     29:         cancellationToken.ThrowIfCancellationRequested();
     30:     }
     32:     return default(T);
     33: }

    In this last version I don't pick a random task and wait for it but I rather pick a random completed task, i.e. given the same random number the order in which tasks complete will affect which task is returned.

Page 1 of 1 (2 items)