What else is new in C# 5?

The big new feature in C# 5 is asynchronous programming support, which I wrote about last week. However, the C# folks have also slipped in a couple of smaller features and I thought I’d round things out by mentioning those.

Method caller information

There’s a complete style guide to be written on Writing Enterprisey Code, but one of my favourite “enterprisey” tells, after the use of Visual Basic, is obsessively logging every function you pass through:

Function AddTwoNumbers(a As Integer, b As Integer) As Integer
  Logger.Trace("ArithmeticHelpers", "AddTwoNumbers", "Entering AddTwoNumbers")
  Dim result = OracleHelpers.ExecInteger("SELECT " & a & " + " & b)
  Logger.Trace("ArithmeticHelpers", "AddTwoNumbers", "Calling PrintPurchaseOrders")
  PrintPurchaseOrders()  ' IFT 12.11.96: don't know why this is needed but shipping module crashes if it is removed
  Logger.Trace("ArithmeticHelpers", "AddTwoNumbers", "Returned from PrintPurchaseOrders")
  Logger.Trace("ArithmeticHelpers", "AddTwoNumbers", "Exiting AddTwoNumbers")
  Return result
End Function

Although this code is efficient and clear by enterprisey standards, with C# 5 it can be even efficienter and clearer. C# 4 introduced optional parameters, which meant callers of a method could leave out the arguments and the compiler would fill in the default values:

public void WonderMethod(int a = 123, string b = "hello") { ... }
 
WonderMethod(456);  // compiles to WonderMethod(456, "hello")
WonderMethod();     // compiles to WonderMethod(123, "hello")

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.fff"),  // Lurking 'minutes'/'months' bug introduced during .NET port in 2003 and has not been noticed because nobody ever looks at the log files because they contain too much useless detail
    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 Validation.cs
public void ValidateDatabase() {
  Log.Trace("Entering method");
  // compiles to Log.Trace("Entering method", "Validation.cs", "ValidateDatabase")
  Log.Trace("Exiting method");
}

Notice that the parameters to which you apply the attributes must be optional. If they aren’t optional, the C# compiler will require the calling code to provide them, and the provided values will override the defaults.

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<T>(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 _sprocketSize;
  public int SprocketSize {
    get { return _sprocketSize; }
    set { Set(ref _sprocketSize, value); }  // Compiler fills in "SprocketSize" as propertyName
  }
}

For what it’s worth, you can also get the line number of the calling code using [CallerLineNumber]. This may be useful for diagnostic methods, but if you really need it, that may be a sign that the calling code is just a bit too enterprisey.

Using loop variables in lambdas

Technically, this is a fix to a long-standing cause of confusion and suffering. But it makes C# that bit more usable, so I’m going to mention it anyway.

Since C# 3, it’s been quicker and easier to write anonymous functions than named ones, thanks to lambda syntax. Anonymous functions are widely used in LINQ, but they’re also used in many other cases where you want to quickly parameterise behaviour without investing in some humungous hierarchy of classes and interfaces and virtual functions. An important feature of anonymous functions is that they can capture variables from their local environment. Here’s an example:

public static IEnumerable<int> GetGreaterThan(IEnumerable<int> source, int n) {
  return source.Where(i => i > n);
}

Here, i => i > n is an anonymous function that captures the value of n. For example, if n is 17, then the function is i => i > 17.

In previous versions of C#, if you wrote a loop, you couldn’t use the loop variable in a lambda. Actually, it was rather worse than that. You could use the loop variable in a lambda, but it would give you the wrong results — it would use the value of the loop variable at the time the loop was exited, not at the time the variable was captured.

For example, here’s a function which returns a collection of ‘adder’ functions, one ‘adder’ for each addend in the input:

public static List<Func<int, int>> GetAdders(params int[] addends) {
  var funcs = new List<Func<int, int>>();
  foreach (int addend in addends) {
    funcs.Add(i => i + addend);
  }
  return funcs;
}

Let’s take it for a spin:

var adders = GetAdders(1, 2, 3, 4, 5);
foreach (var adder in adders) {
  Console.WriteLine(adder(10));
}
 
// Printout: 15 15 15 15 15

Clearly this is horribly wrong! Every function in the returned collection has ended up capturing 5 as its addend. This is because they closed over the loop variable, addend, and the final value of the loop variable was 5.

To make this work in C# 3 and 4, you have to remember to copy the loop variable into a local variable (within the scope of the loop), and have your lambda close over the local variable:

foreach (var addend_ in addends) {
  var addend = addend_;  // DON'T GO NEAR THE LOOP VARIABLE
  funcs.Add(i => i + addend)
}

Because the functions are closing over a local variable rather than the loop variable, the value is now preserved and you get the correct results.

This isn’t an obscure edge case by the way — I’ve come up against it numerous times in my projects. A more realistic example from one project is building a function to perform filtering. The function is built up from a collection of Restriction objects specified by the user. The code loops over the Restriction objects and builds up a list of functions representing the clauses (e.g. Name Equals “BOB” becomes r => r["Name"] == "BOB"), then combines these functions into a final filter function which runs all of the clauses and checks they are all true. My first pass at this didn’t work because each clause function ended up closing over the same Restriction object — the last one in the collection.

In C# 5, this is fixed and you can close over loop variables and get the results you expect. If you like to take advantage of C#’s hybrid OO-functional nature, this removes a nasty bear trap that has been causing problems for years.

So that’s it for C# 5. From a language point of view, there’s not a whole lot of new stuff to learn, though the async and await keywords conceal a great deal of depth. Happy coding!

Tagged as General

41 Responses to “What else is new in C# 5?”

  • Great article…just one remark…I am not sure “efficienter” is a proper word :)

  • […] What else is new in C# 5? (Ivan Towlson) […]

  • There’s some incredibly annoying tweet\share bar that’s impossible to close and ruins your page. I can’t even see what I’m writing. Friendly suggestion, get rid of it :)

  • Tweet bar to the left on mine.. no issues

    Article is great thanks :)

  • Hi,

    one doubt remains like

    following line
    ///////////////////////////////
    set { Set(ref _sprocketSize, value); } // Compiler fills in “SprocketSize” as propertyName
    /////////////////////////////

    should be like this yea ?

    set { Set(ref _sprocketSize, value); } // Compiler fills in “SprocketSize” as propertyName

  • oh Sorry

    should be like this yea ?

    set { Set(>int<(ref _sprocketSize, value); } // Compiler fills in “SprocketSize” as propertyName

  • should be like this yea ?

    set { Set<int>(ref _sprocketSize, value); } // Compiler fills in “SprocketSize” as propertyName

  • […] What else is new in C# 5? – Ivan Towlson discusses two of the other significant changes in C#5, looking at the new method caller information functionality which allows you to easily access details of the calling method, and also looks at a change to how loop variables are captured by lambda expressions, removing a common source of bugs. […]

  • Thanks for sharing! Great article…

  • how does the caller info overlap with AOP/attribute style logging? do you think there will be any use there?

  • This is quite scary – .NET 4.5 is an in-place upgrade and replaces the existing C# compiler, I wouldn’t have expected such a breaking semantics change in this release.

    That said, the C# 2/3/4 behavior is annoying and I can’t imagine anyone using it intentionally (except for demonstration purposes).
    For more details, here’s Eric Lippert’s blog post on the subject: http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

  • Dear Microsoft, Just keep throwing features and more syntax in there. Soon we’ll be at C++ again and code that looks like worse line noise than Perl. Brilliant!

  • That’s great, now I can use loop vars. BUT! My biggest consideration is why MS issued this cr@p at all? Didn’t they know that vars can be from cycles? It was a Perl motto: “it works as you see/expect”. MS decided “let these morons step on rakes, we warned in docs!” instead of preventing these stupid errors.
    It seems MS’ architectors down from hindu trees, taking no care of their “conceptions”.

  • Short & sweet article but the reading experience was utterly ruined by the floating social media tool bar. Please could you put it somewhere else?

  • Related to the lambdas in fors…

    I even created an interface that I use instead of actions to force users of my libraries to create entire classes instead of using the unnamed delegates, as they were getting a lot of errors related to the for variables, but were complaining that was an error of my library.
    Now I can create the version that support actions again.

  • indeed ;}

  • To user WTF… If the “tweet\share bar” is in your way, you should be ashamed to out yourself on your crappy resolution. :)

    Anyway, good blog!

  • Get that frickin’ floating Like box out of the way and I may actually read something here. UI fail!

  • […] What else is new in c# 5 This entry was posted in Tyson Zinn and tagged zinn tyson by Tyson Zinn. Bookmark the […]

  • AHHHHH! That floating bar makes your page IMPOSSIBLE to read!!!!

  • good…!!!!!!!!!!

  • I agree about the tweet share bar to the point i did not finish the article

  • @capslock It is in dutch :P
    @WTF suggestion: make your browser and/or resolution bigger. it’s not in the way anymore at 1360×768

  • The social media bar is a serious beating. I spread the browser over two screens to get enough room to read the article.

  • Thanks. Useful article.

  • @Mudasir: No, C# generic type inference will figure out the generic type, so it’s safe to write plain Set(ref _f, v). If _f and v are ints then C# will figure out that T must be int, and fill it in automatically.

  • @john: I think that would depend on how your aspect weaver was implemented. I would expect that the caller info attributes would not be that useful in AOP/attribute-based logging because the weaver would be inserting that information anyway.

  • FYI, the share link on your site totally screws up reading your blog: http://cl.ly/3f2b0g1s3E3s3d023R0H

  • Great!
    The overloading was sold as something superior to optional parameters when MS switched us from VB6 to C#.NET. Now, ten yeas later, we are sold optional parameters as solution superior to overloading!
    How much do they pay to promote that? May be I need to apply for that job?

  • @Nick, I don’t think Microsoft tried to sell anyone on overloading and not optional parameters, or even the other way around. I don’t think they had time to add everything in that users may or may not want. Anyway Microsoft didn’t switch you from VB6 to C#, they made VB.net for you, and it had syntax for optional parameters in 2003 at least right?

  • Sorry about the social buttons everyone. I’ve changed it so the buttons are integrated into the post for now so you should be able to read without a problem.

    Thanks for all the comments!

    John-Daniel

  • Excellent. I really like the INPC code – I’ll be using that for sure, and now you can close over loop variables I look forward to not declaring a local variable when I am using lambdas inside a loop (very common for me too!)

  • I don’t think it’s exactly accurate to say that you couldn’t use a loop variable in a lambda, you could and it would work perfectly well provided you understood what was happening and when.

    The value is captured when the lambda is executed, not when it’s defined. There are times when the behaviour is useful. You can define a query once, closing over a local variable, then on each iteration of a loop alter the variable and execute the query getting different results without having to redefine the query every time.

    It’s a useful, if not obvious, behaviour. I think it’s good that they’ve changed it to something more obvious as it’s one less place where deferred execution can trip you up, but I don’t think it’s right to dismiss the original functionality simply as wrong.

  • @Dave: And that useful behaviour won’t change, because that is closing over a local variable which is then *used* within the loop. All that’s changing is the surprising behaviour of creating a closure *within the loop* over the *loop variable* itself, i.e. the range variable of a loop statement (e.g. the x in a foreach (x in…)). Locals that are declared and closed over outside the loop, then modified within with loop, retain the old behaviour.

    And the change remains consistent with the behaviour you describe: the change is that for closure purposes loop variables are considered local to each iteration of the loop (like variables declared in the loop body) rather than local to the method that contains the loop. If they had changed the behaviour of locals, I would completely agree with you; but since they are only changing the behaviour of loop range variables, I’ll stand by describing the original functionality as wrong, because the result — multiple closures each using the final value of the loop variable — was not useful, and was never what anyone actually intended when they created a closure within a loop.

    Hope this makes more sense now, and sorry if the original post was not clear about what was changing!

  • […] What else is new in C# 5? » Mindscape Blog » Blog Archive – Links.NET, .net5, alarm, alternatives, android, apps, BBC, c#, camera, clock, development, earth, infographics, instagram, mobilephone, performance, photography, programming, regex, regularexpression, router, scale, science, snapster, Solar System, solarsystem, space, wifi ← links for 2011-09-07 Leave a comment ?0 Comments. /* */ […]

  • […] […]

  • Very helpful article. Love the ability to add Lambda syntax to OnPropertyChanged this way.

  • The MethodCaller stuff is cool for implementing IPropertyInfoChanged event but it is only half finished if you want to use it in production tracing with a reasonable Api which accepts format strings. For more see here: http://geekswithblogs.net/akraus1/archive/2012/04/09/149264.aspx

  • ‘Caller information’ is a barely useful feature, given this is possible now…….

    private void WriteTrace(string message)
    {
    StackTrace stackTrace = new StackTrace(true);
    StackFrame frame = stackTrace.GetFrame(1);

    string callerMemberName = frame.GetMethod().Name;
    string callerFilePath = frame.GetFileName();
    int callerLineNumber = frame.GetFileLineNumber();

    //ETC

    }

  • I personally wonder if there is any need for microsoft to develope and evelove such complicated features for us programmer?

    I do things better in much simpler old fashioned code styles rather than using complicated new methods.

  • Great article, I always love lambda. Now with loop variables in lambda, it becomes greater.

  • Leave a Reply

Archives

Join our mailer

You should join our newsletter! Sent monthly:

Back to Top