Using a string as IEnumerable

Sebastian Negraszus picture Sebastian Negraszus · Jan 30, 2013 · Viewed 19k times · Source

In .NET for Windows Store Apps –it seems– you cannot use strings as Enumerables anymore. The following code works for desktop applications, but not for apps:

public static bool SolelyConsistsOfLetters(string s)
{
    return s.All(c => char.IsLetter(c));
}

The error is

'string' does not contain a definition for 'All' and no extension method 'All' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)

But I'm not missing an assembly reference or using System.Linq;. The following code does work:

public static IEnumerable<char> StringAsEnumerable(string s)
{
    foreach (char c in s)
    {
        yield return c;
    }
}

public static bool SolelyConsistsOfLetters(string s)
{
    return StringAsEnumerable(s).All(c => char.IsLetter(c));
}

The problem is, s as IEnumerable<char> does not work (error: "Cannot convert type 'string' to 'System.Collections.Generic.IEnumerable' (..)") and s.GetEnumerator() is not available.

My questions:

  1. Is there really no elegant way to use strings as Enumerables (without a helper method as above)? I feel like I must be missing something totally obvious.
  2. Since the string does not behave like an Enumerable, why/how does foreach work here?

Answer

Jo&#227;o Angelo picture João Angelo · Jan 30, 2013

The String.IEnumerable<Char>.GetEnumerator method is not supported in .NET for Windows Store applications, however, the non generic String.IEnumerable.GetEnumerator is supported so that's why the foreach approach works.

Based on that I believe it should also be possible to do:

s.Cast<char>().All(c => char.IsLetter(c))

UPDATE (regarding Jani comment) The foreach is already performing the cast by defining the each variable as char. The nongeneric IEnumerable version returns object and at compile time every cast from object to any other type is acceptable so that's why it works.

The following code will also compile fine but will fail at runtime:

var valid = new object[] {'1', '2', '3'};

foreach (char c in valid)
    Console.WriteLine(c);

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

foreach (char c in invalid)
    Console.WriteLine(c); // Fails at runtime; InvalidCastException