Tuesday, 20 March 2012

C# 5 attributes on optional parameters

With C# 5, you can put a special attribute on an optional parameter and the compiler will fill in the value not with a constant but with information about the calling method. This means we can implement the Logger.Trace to automagically pick up where it’s being called from:
public static void Trace(string message,
                         [CallerFilePath] string sourceFile = "",
                         [CallerMemberName] string memberName = "")
{
    string msg = String.Format("{0}: {1}.{2}: {3}",
       DateTime.Now.ToString("yyyy-mm-dd HH:MM:ss"),
       Path.GetFileNameWithoutExtension(sourceFile), memberName, message);

    LoggingInfrastructure.Log(msg);
}

Now, if the caller calls Log.Trace("some message") the compiler will fill in the missing arguments not with the empty string, but with the file and member where the call happens:

// In file called Fred.cs
public void SomeFunc()
{
  Log.Trace("Hello");
  // Compiles to Log.Trace("Hello", "Fred.cs", "SomeFunc")
}

Another example of how you can use this is in implementing INotifyPropertyChanged without needing either literal strings, expression magic or mystic weavers:
public class ViewModelBase : INotifyPropertyChanged 
{
  protected void Set(ref T field,
                        T value,
                        [CallerMemberName] string propertyName = "") 
  {
    if (!Object.Equals(field, value)) 
    {
      field = value;
      OnPropertyChanged(propertyName);
    }
  }
  // usual INPC boilerplate
}
 
public class Widget : ViewModelBase 
{
  private int _thingy;
  public int Thingy
  {
    get { return _thingy; }
    set { Set(ref _thingy, value); } // Compiler fills in "Thingy" as propertyName
  }
}
I grabbed this info from http://www.mindscapehq.com/blog/index.php/2012/03/18/what-else-is-new-in-c-5/. More can be found there, but I thought this feature important/cool enough to blog about it on its own right. And mainly so I don't forget about it...