C# Special Method Names

Introduction

You may not have realized that some of the patterns that you’ve been using for ages in your C# programs are based on conventions, rather than on a specific API. What I mean by this is, some constructs in C# are based on some magical methods with well-defined names which are not defined in any base class or interface, but yet just work. Let’s see what they are.

Enumerations

You are probably used to iterating through collections using the foreach statement. If you are, you may know that foreach actually wraps a call to a method called GetEnumerator, like the one that is defined by the IEnumerable and IEnumerable<T> interfaces. Thus, you might think, the magic occurs because the collection implements one or the two, but you would be wrong: it turns out that in order to iterate through a class using foreach all it takes is that the class exposes a public method called GetEnumerator that returns either a IEnumerator or a IEnumerator<T> instance. For example, this works:

public class Enumerable
{
    public IEnumerator GetEnumerator()
    {
        yield return 1;
        yield return 2;
        yield return 3;
    }
}

var e = new Enumerable();

foreach (int i in e) { /*...*/ }

As you see, there is no need to implement any of these interfaces, but it is a good practice, for example, because it gives you access to LINQ extension methods.

Deconstruction to Tuples

Tuples were introduce in C# 7. In a nutshell, they provide a way for us to return multiple values from a method:

(int x, int y) GetPosition()
{
    return (x: 10, y: 20);
}

Another option is to have a class deconstructed into a tuple. Say, for example, that we have a class like this:

public class Rectangle
{
    public int Height { get; set; }
    public int Width { get; set; }
}

We can have it deconstructed into a tuple, by providing one or more Deconstruct methods in this class:

public void Deconstruct(out int h, out int w)
{
    h = this.Height;
    w = this.Width;
}

Which allows you to write code like this:

var rectangle = new Rectangle { Height = 10, Width = 20 };
var (h, w) = rectangle;

You can implement multiple Deconstruct methods with different parameters, which must always be out. When you try to assign your class to a tuple, C# will try to find a Deconstruct method that matches the tuple’s declaration, or throw an exception if one cannot be found:

public void Deconstruct(out int perimeter, out int area, out bool square)
{
    perimeter = this.Width * 2 + this.Height * 2;
    area = this.Width * this.Height;
    square = this.Width == this.Height;
}

var (perimeter, area, square) = rectangle;

Collection Initialization

Since C# 6, we have a more concise syntax for initializing collections:

var strings = new List<string> { "A", "B", "C" };

The syntax to follow is an enumeration of items whose type matches the collection’s item type, inside curly braces, each separated by a comma. This is possible because there is a public Add method that takes a parameter of the appropriate type. What happens behind the scene is that the Add method is called multiple times, one for each item inside the curly braces. Meaning, this works too:

public class Collection : IEnumerable
{
    public IEnumerator GetEnumerator() => /* ... */
    public void Add(string s) { /* ... */ }
}

Notice that this collection offers a public Add method and needs to implement either IEnumerable or IEnumerable<T>, which, mind you, do not define an Add method. Having this, we can write:

var col = new Collection { "A", "B", "C" };

The magical Add method can have multiple parameters, like for dictionaries:

var dict = new Dictionary<string, int> { { "A", 1 }, { "B", 2 }, { "C", 3 } };

Each parameter will need to go inside it’s own set of curly braces.

What’s even funnier is, you can mix different Add methods with different parameters:

public void Add(int i) { /* ... */ }
public void Add(string s) { /* ... */ }

var col = new Collection { 1, 2, 3, "a", "b", "c" };

Conclusion

In this post I introduced the magical GetEnumerator, Deconstruct and Add methods. This was just for fun, the information here is probably useless, but, hey, it’s done! Winking smile

                             

2 Comments

  • Another magic method is GetAwaiter.

    Support for LINQ-syntax expressions can be implemented similarly.

    These can be added to types as extension methods. I'm not sure if this applies to all of them.

  • Hi, Robert!
    You're probably right, that's what await does underneath. Thanks!

Add a Comment

As it will appear on the website

Not displayed

Your website