tags:

views:

437

answers:

14

I'd like to learn what are all the convenience features of C#, and how they map to C#.

For example, automatic properties:

public string Foo { get; set; }

...maps to something like this:

string <Foo>k__BackingField;

[CompilerGenerated]
public string Foo {
   get { return this.<Foo>k__BackingField; } 
   set { this.<Foo>k__BackingField = value; } 
}

Foreach loops:

foreach(char c in "Hello") {
   Console.WriteLine(c);
}

...maps to something like this (I think):

CharEnumerator en;

try {

   en = "Hello".GetEnumerator();

   while (en.MoveNext()) {
      char c = en.Current;

      Console.WriteLine(c);
   }

} finally {

   IDisposable disp = en as IDisposable;

   if (disp != null)
      disp.Dispose();
}

The disposing of the enumerator makes foreach very useful when dealing with unmanaged resources, like looping through lines in a file, or records in a database.

I think a good understanding of these high level features can help us write better code. What are other convenience features of C#, and how do they map to C#?

+3  A: 

Object initializers are pretty syntax sugar:

 public class Book
{
 public string Author { get; set; }
 public string Title { get; set; }
}

Book book = new Book() {Author = "John Smith", Title = "C# Syntax"};

The above is another way to write:

Book book = new Book();
book.Author = "John Smith";
book.Title = "C# Syntax";

Implicit types:

var book = new Book() {Author = "John Smith", Title = "C# Syntax"};
Kris Krause
how does this map to C# without using object initializers?
Max Toro
should be pretty obvious: invoking setters one by one right after the ctor call.
IgorK
BTW "anonymous type" example doesn't look right for me, because you have a normal Book type there. is it typo and should be `var book = new {Author = "John Smith", Title = "C# Syntax"};` instead? :)
IgorK
That wasn't an anonymous type example. It was implicit typing.
yodaj007
lgorK - you are correct. Sorry about that... fixed/editted.
Kris Krause
+1  A: 

Lambda expressions are another example.

var myZombies = myMonsters.Where(monster => monster.Kind == MonsterKind.Zombie);

pulls out the Lambda delegate into its own method with a weird name.

You can use the Reflector and switch between the C# versions in the options dialogue to see the differences of how such things would be resolved.

herzmeister der welten
Strictly speaking it only does this for delegate types (the above syntax could also work with `Expression` types, but is implemented completely differently)
Marc Gravell
+7  A: 

using is syntax sugar

using(x) 
{
    ...
}

Maps to

try {
    // Code
}
finally
{
    if(x != null)
        ((IDisposable)x).Dispose();
}

The from- thing in LINQ is syntactic sugar

from x in l where y select z;

translates to

l.Where(x => y).Select(x => z);
Arve
This is close but not quite correct. It actually calls ((IDisposable)x).Dispose().
womp
Updated code sample to make it more correct
Arve
+9  A: 

If you want to talk about the amount of code saved, the biggest savers (IMO) are:

iterator blocks

Example:

public static IEnumerable<int> Get() {yield return 1; yield return 2;}

Saved:

  • writing an IEnumerable<T> implementation
  • writing an IEnumerator<T> implementation
  • handling state of the enumerator; have you seen how horrible it is? Seriously.

captured variables (anon methods / lambdas)

Example:

var list2 = list1.FindAll(x => (x.Value % 10) == find);

Saved:

  • writing a class to hold the state
    • in complex cases, nested state
  • initializing the state wrapper
  • writing a method on that class

Expression lambda compiler

Example (from above):

Expression<Func<Foo,bool>> pred = x => (x.Value % 10) == find;

Saved:

  • yeuch - lots of tree code; very, very complex; in the above I can see (in no particular order) Expression.Parameter, Expression.Property, Expression.Field, Expression.Constant, Expression.Equal, Expression.Modulo, Expression.Lambda... combine ;-p
Marc Gravell
+6  A: 

LINQ as a whole (especially using the LINQ query syntax) is essentially a major C#/.NET convenience feature that replaces for-loops/iterators/complex query logic much of the time.

Noldorin
+4  A: 
  1. for, while, do...while, switch -- a controversial answer :)

    You could replace it with some code based on if and goto.

  2. lock(){} could be translated to Monitor.Enter() and Monitor.Exit() within a try-finally with null check (see http://stackoverflow.com/questions/590159/in-c-does-a-locked-object-stay-locked-if-an-exception-occurs-inside-it/590174#590174 )

IgorK
+4  A: 

The ?: operator, which tests a boolean or boolean expression and returns the first value if the test returns true and the second value if the test returns false:

condition ? first_expression : second_expression

Which essentially means:

var x;
if (condition)
{
    x = first_expression;
}
else
{
    x = second_expression
}

Using the ?: operator, you can write int x = condition ? 10 : 20; instead of having to write out the whole if statement.

Zach Johnson
+2  A: 

The null coalescing operator (??) can be pretty nifty. This ...

ISomeRef myRef = firstChoice ?? secondChoice ?? fallback;

Translates to something functionality equivalent to:

ISomeRef myRef = firstChoice;
if(myRef == null) myRef = secondChoice;
if(myRef == null) myRef = fallback;

I say functionally equivalent because I haven't looked in reflector yet :)

Mark Simpson
+2  A: 

Anonymous types:

var message = new { Message = "Hello, world!", MessageID = 1 };

is translated to

<>f__AnonymousType0<string, int> message =
    new <>f__AnonymousType0<string, int>("Hello, world!", 1);

where

[CompilerGenerated]
[DebuggerDisplay(
    @"\{ Message = {Message},
    MessageID = {MessageID} }",
    Type="<Anonymous Type>"
 )
]
internal sealed class <>f__AnonymousType0<
    <Message>j__TPar,
    <MessageID>j__TPar
> {
    // Fields
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly <Message>j__TPar <Message>i__Field;
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private readonly <MessageID>j__TPar <MessageID>i__Field;

    // Methods
    [DebuggerHidden]
    public <>f__AnonymousType0(
        <Message>j__TPar Message,
        <MessageID>j__TPar MessageID
    ) {
        this.<Message>i__Field = Message;
        this.<MessageID>i__Field = MessageID;
    }

    [DebuggerHidden]
    public override bool Equals(object value) {
        var type = value as <>f__AnonymousType0<
            <Message>j__TPar,
            <MessageID>j__TPar
        >;
        return (((type != null) && 
            EqualityComparer<<Message>j__TPar>.Default.Equals(
                this.<Message>i__Field, 
                type.<Message>i__Field)
            ) &&
            EqualityComparer<<MessageID>j__TPar>.Default.Equals(
                this.<MessageID>i__Field,
                type.<MessageID>i__Field)
            );
    }

    [DebuggerHidden]
    public override int GetHashCode() { 
        int num = 0x2e22c70c;
        num = (-1521134295 * num) +
            EqualityComparer<<Message>j__TPar>.Default.GetHashCode(
                this.<Message>i__Field
            );
        return ((-1521134295 * num) +
            EqualityComparer<<MessageID>j__TPar>.Default.GetHashCode(
                this.<MessageID>i__Field)
            );
    }

    [DebuggerHidden]
    public override string ToString() {
        StringBuilder builder = new StringBuilder();
        builder.Append("{ Message = ");
        builder.Append(this.<Message>i__Field);
        builder.Append(", MessageID = ");
        builder.Append(this.<MessageID>i__Field);
        builder.Append(" }");
        return builder.ToString();
    }

    // Properties
    public <Message>j__TPar Message {
        get {
            return this.<Message>i__Field;
        }
    }

    public <MessageID>j__TPar MessageID {
        get {
            return this.<MessageID>i__Field;
        }
    }
}
Jason
+1  A: 

Nullable<T> is another incredible convenience. Especially handy is the ? syntax as in int? is short for Nullable<int>.

Jason
Plus the ? shortcut for Nullable<T>, as in int? or bool?
Zach Johnson
@Zach Johnson: Oh, excellent point.
Jason
+3  A: 

Anonymous delegates get changed into methods during compilation, so this:

var collection = new[] { 1, 2, 3, 4 }.Where(i => i == 2);

causes a new method to be created:

[CompilerGenerated]
private static bool <Main>b__0(int i)
{
    return (i == 2);
}

and changes to:

IEnumerable<int> collection = new int[] { 1, 2, 3, 4 }.Where<int>(<Main>b__0);

Notice that it uses a method name that you cannot create before compiling <Main>b__0 - you cannot have <> in a method name, so it always avoid collisions.

The same process happens to this as well as i => i == 2:

delegate(int i) { return i == 2; }

You will also notice that you can use short hand for:

new[] { 1, 2, 3, 4 }
// goes to...
new int[] { 1, 2, 3, 4 }

and

var foo = new Foo();
// goes to...
Foo foo = new Foo();
Callum Rogers
+1  A: 

Extension methods.

If this is your extension method:

static class ObjectExtensions
{
    public void ExtensionMethod(this object obj)
    {
        // Do something here...
    }
}

And you call obj.ExtensionMethod()

It translates to:

ObjectExtensions.ExtensionMethod(obj)

If you happen to have a version of the C# compiler that supports extension methods, you can trick the compiler into letting you use extension methods in versions of .Net prior to 3.5 by creating a System.Runtime.CompilerServices.ExtensionAttribute, like this: (It is perfectly safe to do this, since the compiler maps the extension methods into standard IL)

namespace System.Runtime.CompilerServices
{
    using System;

    internal sealed class ExtensionAttribute : Attribute
    {
    }
}
Zach Johnson
A: 

Unusual amongst high level languages, C# has a 'goto' statement and consequently several other C# statements can be considered to be language translations into the goto statement.

For example, the if() statement maps directly to a goto while the standard for() loop actually maps to a while() loop which maps to a goto.

The switch() statement is unusual as it is compiled differently depending on number of cases and type of the switch argument. Thus it is not possible to consider it a language transformation to goto.

RichB
A: 

Collection initializers (and type inference):

var scoobies = new List<Scooby>()
        {
            new Scooby("Buffy"),
            new Scooby("Willow"),
            new Scooby("Xander")

        };

Maps to:

List<Scooby>() scoobies = new List<Scooby>();
scoobies.Add(new Scooby("Buffy"));
scoobies.Add(new Scooby("Willow"));
scoobies.Add(new Scooby("Xander"));
Rodrick Chapman