Friday, July 17, 2015

C# Using Static statement

C# 6.0 introduced the "using static" statement, which allows developers to specify a static class (as well as enums and structs) rather than a namespace in a using statement. Many examples describing this new feature use System.Console as an example.
using static System.Console;
using static System.IO.File;

public static void Main(string[] args)
{
  WriteLine("Some value");
}
It is not clear where "Some value" is being written in the above code. In this case System.IO.File does not have a WriteLine method, so we can deduce that System.Console.WriteLine is being called. This becomes much less obvious when working on large or unfamiliar code bases. I therefore initially thought the "using static" statement would only make code less readable, not more. However, I have recently encountered a situation where I found the "using static" statement to be very useful.

First a bit of context. When developing WCF web services, I usually create three separate projects

  • BusinessDomain.Service.Wcf - Contains contract classes, interfaces, and DTO mapping extension methods.
  • BusinessDomain.Service.Wcf.Host - Contains service implementation.
  • BusinessDomain.Service.Wcf.Proxy - Contains client implementation.

The BusinessDomain.Service.Wcf project contains static classes with extension methods which map values between DTO objects and other objects. For example, with a Person domain class, and a Person DTO, the following ToDomainEntity method would map values from the DTO object to the domain object.
namespace BusinessDomain.Service.Wcf.EntityMappers
{
  public static class PersonMapper
  {
    public static Person ToDomainEntity(this PersonDTO source)
    {
      // map values from PersonDTO to Person
    }
  }
}

This method is then used to easily convert the DTO to a domain object when interacting with the web service. The BusinessDomain.Service.Wcf.Proxy project might contain code similar to the following, to interact with the service and return an object to the client. Note the using statement with the namespace of the mapping classes and extension methods.
using BusinessDomain.Service.Wcf.EntityMappers;

namespace BusinessDomain.Service.Wcf.Proxy
{
  public class PersonServiceProxy
  {
    public Person FindPerson(int personId)
    {
      PersonDTO response = Svc.FindPerson(personId);
      
      return response.ToDomainEntity();
    }
  }
}

This approach works well for WCF projects, but I recently tried the same approach with a Web API project. With Web API, all of the service responses are of type HttpResponseMessage. Adding extension methods for HttpResponseMessage quickly becomes unwieldy because every extension method shows up in Intellisense, including those that are not appropriate in the current context. Expanding on the WCF example above, imagine I have a Person class and a Car class, along with mapper classes for each.
namespace BusinessDomain.Service.WebApi.EntityMappers
{
  public static class PersonMapper
  {
    public static Person ToPerson(this HttpResponseMessage source)
    {
      // map values
    }
  }

  public static class CarMapper
  {
    public static Car ToCar(this HttpResponseMessage source)
    {
      // map values
    }
  }
}

using BusinessDomain.Service.WebApi.EntityMappers;

namespace BusinessDomain.Service.WebApi
{
  public class PersonServiceProxy
  {
    public Person FindPerson(int personId)
    {
      HttpResponseMessage response = Svc.FindPerson(personId);

      return response.ToPerson();
    }
  }
}
In the FindPerson method, the Intellisense for the response object is populated with both ToPerson and ToCar. Calling the ToCar extension method would never be appropriate in this context. In this situation, the "using static" method can be used to limit the Intellisense items to the methods appropriate to the current context.
using BusinessDomain.Service.WebApi.EntityMappers;
using static BusinessDomain.Service.WebApi.EntityMappers.PersonMapper;

namespace BusinessDomain.Service.WebApi
{
  public class PersonServiceProxy
  {
    public Person FindPerson(int personId)
    {
      HttpResponseMessage response = Svc.FindPerson(personId);

      return response.ToPerson();
    }
  }
}
The Intellisense for the response object would still contain the ToPerson extension method, but would no longer be cluttered with the ToCar or other inappropriate extension methods.


No comments:

Post a Comment