Ozcode C# Riddle #1: What’s Your Type?

Ozcode C# Riddle

This is the first in our series of Ozcode C# riddles. In these riddles, we explore some of the deep, dark corners of the language to help you hone your problem-solving skills. Along the way, you’ll meet some C# debugging tools, and also develop some best practices for handling errors in C#.

C# is a strongly typed language … for the most part. It uses static typing to enforce type safety at compile time, but since the introduction of the dynamic keyword in C#4, C# supports dynamic typing, and type safety is only enforced at runtime. When it comes to collections, things can get a bit tricky. Working with collections can involve anything from iterating through a simple list of items, to searching through a large and complex object graph. One way to traverse collections is to use an enumerator, and that’s in the core of today’s riddle.

Consider a collection of integers 1, 2, and 3. You can declare it as an array:

var arr = new[] { 1, 2, 3 };

You can also declare it using a generic List type (List<T>):

var list = new List { 1, 2, 3 };

Which declaration you use can have a huge impact on how your code runs.

The Riddle

What is the output of the code snippet below? (Answer below)

To solve this riddle, you need to know about:

  • collections, especially the process of iterating through them
  • the difference between value types and reference types

using System;
using System.Collections;
using System.Collections.Generic;

public class Program 
{
  public static void Main() 
  {
    var arr = new [] {1, 2, 3};
    var list = new List < int > {1 ,2, 3};

    var arrEnumerator = arr.GetEnumerator();
    var listEnumerator = list.GetEnumerator();

    MoveNext(arrEnumerator);
    MoveNext(arrEnumerator);
    MoveNext(arrEnumerator);

    Console.WriteLine();

    MoveNext(listEnumerator);
    MoveNext(listEnumerator);
    MoveNext(listEnumerator);
  }

  private static void MoveNext(IEnumerator enumerator) 
  {
    if (enumerator.MoveNext()) 
    {
      Console.WriteLine(enumerator.Current);
    }
  }

  private static void MoveNext(IEnumerator < int > enumerator) 
  {
    if (enumerator.MoveNext()) 
    {
      Console.WriteLine(enumerator.Current);
    }
  }
}
Enhance Visual Studio Debugging - Ozcode

The Solution

So, your knee-jerk response would be that the output is:

1
2
3

1
2
3

But that’s not the case.

If you don’t believe me, copy the snippet into your favorite IDE and run it.

So, what’s going on? On the face of it, in both cases, you’re just iterating through a set of integers. That may be true but iterating through an array is not the same as iterating through a generic List. All collections in C# have a GetEnumerator() method which returns an enumerator object that lets you iterate through the items of the collection. However, not all enumerators have the same type.

The behavior of the array is very intuitive. Invoking  MoveNext(arrEnumerator) three times iterates the array enumerator so it’s pointing to the last element, and getCurrent returns 2 as expected. So why doesn’t it work like that for the listEnumerator?

Well, the enumerator for a generic list is a struct:

public struct List.Enumerator : System.Collections.Generic.IEnumerator

And a struct is a value type.

Now, here’s the catch. In case you didn’t notice, we’re not iterating our two enumerators directly by calling their MoveNext methods, we’re doing it indirectly through our own MoveNext methods. When you pass a Value type to a method, the method doesn’t operate on the parameter directly, but on a copy of the parameter. So, when we apply MoveNext on the List enumerator (within our own MoveNext method), we’re iterating a copy of the enumerator rather than the original enumerator. This is known as boxing in C#. Consequently, the original enumerator remains at its starting point– before the first element of the list.

So, the output of this program is: (drumroll)


1
2
3

1
1
1

(Ba dah boom)

Don’t believe me? Check here.

Why doesn’t this happen for the array?

Since the array enumerator is a Reference type, when we pass it to our MoveNext method, the method does not create a copy, but rather operates directly on the object. Consequently, the array enumerator iterates through the integers to the last value which is returned with Current.

Why worry about enumerator types?

It’s rare to need to a collection’s enumerator directly. In my 10 years of experience programming in C#, I haven’t had to do that more than once or twice.  A more common (and easier) way to iterate through the elements of a collection (and a c# coding best practice) is to use a foreach loop which manages reference types and value types internally, so you don’t have to worry about it.


using System;
using System.Collections.Generic;

public class Program 
{
  public static void Main() 
  {
    var arr = new [] {1, 2, 3};
    var list = new List < int > {1, 2, 3};

    foreach(var i in arr) 
    {
      Console.WriteLine(i);
    }

    Console.WriteLine();

    foreach(var i in list) 
    {
      Console.WriteLine(i);
    }
  }
}
And now the result is as expected:

1
2
3

1
2
3
You really wanna check?

Main Takeaways

Your main takeaways from this riddle are:

  • Don’t use enumerators unless you really have to. Use a foreach loop instead.
  • When you pass a parameter to a method, make sure you know if it’s a Value type or a Reference type.

Ozcode - Join Us

 

Alexander Seleznyov

Comments

Keep your code in good shape!

Subscribe to our blog and follow the latest news in the .NET debugging industry

Ready to Dive into Your Prod Code?

Easy debugging with full time-travel data

The Exception

A Quarterly Roundup

Subscribe to our quarterly newsletter and hear all about what’s happening around debugging with Ozcode

Share on facebook
Share on twitter
Share on linkedin
Share on email

Recent Posts

Follow Us

Join Ozcode YouTube Channel

Let’s start debugging, it’s free!

Thanks for downloading the OzCode trial!

You’re well on your way to making C# even sharper.

If your download doesn’t start automatically , please use this direct link.

If you’d like to install OzCode but don’t have
administrative privileges on your machine, please contact us.

Get Started for FREE!

Ozcode Logo

This website uses cookies to ensure you get the best experience on our website.