22

What is the difference between ToCharArray and ToArray

string mystring = "abcdef";

char[] items1 = mystring.ToCharArray();
char[] items2 = mystring.ToArray();

The result seems to be the same.

2
  • referencesource.microsoft.com/#mscorlib/system/… Commented May 10, 2016 at 12:51
  • 1
    ToCharArray() and ToArray() are generic methods and in other class will produce different results. In the string class they produce the same results. Character and string are two byte entities, but other classes are not two bytes. So when you have a single byte array ToCharArray() will put data into every other byte of the destination while ToArray() will put results into every byte. Commented May 10, 2016 at 12:56

3 Answers 3

55

string.ToCharArray() is a member of the string class.

string.ToArray() is actually using a ToArray() extension of IEnumerable<T>, taking advantage of the fact that string implements IEnumerable<char>.

Of the two, string.ToCharArray() is likely to be more performant.

From the C# reference source, the implementation of string.ToCharArray() is:

unsafe public char[] ToCharArray() {
    // <
    int length = Length;
    char[] chars = new char[length];
    if (length > 0)
    {
        fixed (char* src = &this.m_firstChar)
            fixed (char* dest = chars) {
                wstrcpy(dest, src, length);
            }
    }
    return chars;
}

Also from the C# reference source, the implementation of IEnumerable<T>.ToArray() is:

public static TSource[] ToArray<TSource>(this IEnumerable<TSource> source) {
    if (source == null) throw Error.ArgumentNull("source");
    return new Buffer<TSource>(source).ToArray();
}

...

struct Buffer<TElement>
{
    internal TElement[] items;
    internal int count;

    internal Buffer(IEnumerable<TElement> source) {
        TElement[] items = null;
        int count = 0;
        ICollection<TElement> collection = source as ICollection<TElement>;
        if (collection != null) {
            count = collection.Count;
            if (count > 0) {
                items = new TElement[count];
                collection.CopyTo(items, 0);
            }
        }
        else {
            foreach (TElement item in source) {
                if (items == null) {
                    items = new TElement[4];
                }
                else if (items.Length == count) {
                    TElement[] newItems = new TElement[checked(count * 2)];
                    Array.Copy(items, 0, newItems, 0, count);
                    items = newItems;
                }
                items[count] = item;
                count++;
            }
        }
        this.items = items;
        this.count = count;
    }

    internal TElement[] ToArray() {
        if (count == 0) return new TElement[0];
        if (items.Length == count) return items;
        TElement[] result = new TElement[count];
        Array.Copy(items, 0, result, 0, count);
        return result;
    }
}        

As you can see, that's a LOT more complicated!

Why doesn't IEnumerable<T>.ToArray() use the optimised path?

There's one other thing we need to explain.

If you inspect the implementation of Buffer<T> you'll see this optimisation:

ICollection<TElement> collection = source as ICollection<TElement>;
if (collection != null) {
    count = collection.Count;
    if (count > 0) {
        items = new TElement[count];
        collection.CopyTo(items, 0);
    }
}    

You could reasonably ask why that path isn't taken? If it was, this would be a good optimisation for string.ToArray().

Well, the answer is simply: A string doesn't implement ICollection<T> and therefore source as ICollection<TElement> will return null, and that optimisation will not be done.

Even worse, the non-optimised path through Buffer<T> will use the string enumerator, which is implemented as follows:

public sealed class CharEnumerator : IEnumerator, ICloneable, IEnumerator<char>, IDisposable 
{
    private String str;
    private int index;
    private char currentElement;

    internal CharEnumerator(String str) {
        Contract.Requires(str != null);
        this.str = str;
        this.index = -1;
    }

    public Object Clone() {
        return MemberwiseClone();
    }

    public bool MoveNext() {
        if (index < (str.Length-1)) {
            index++;
            currentElement = str[index];
            return true;
        }
        else
            index = str.Length;
        return false;

    }

    public void Dispose() {
        if (str != null)
            index = str.Length;
        str = null;
    }

    /// <internalonly/>
    Object IEnumerator.Current {
        get {
            if (index == -1)
                throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
            if (index >= str.Length)
                throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));                        

            return currentElement;
        }
    }

    public char Current {
        get {
            if (index == -1)
                throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
            if (index >= str.Length)
                throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));                                            
            return currentElement;
        }
    }

    public void Reset() {
        currentElement = (char)0;
        index = -1;
    }
}


ICollection<TElement> collection = source as ICollection<TElement>;
if (collection != null) {
    count = collection.Count;
    if (count > 0) {
        items = new TElement[count];
        collection.CopyTo(items, 0);
    }
}    

This introduces a whole other level of inefficiency.

The moral of this story

Never use IEnumerable<char>.ToArray() instead of string.ToCharArray()!

Sign up to request clarification or add additional context in comments.

4 Comments

It would be a bit more educational if you included an explanation on why ToArray() would be less efficient: string doesn't implement ICollection<char>, which means ToArray() doesn't know the length ahead of time, which means it will create a lot of intermediate buffers. (Otherwise one could reasonably assume that LINQ is "smart enough" to choose the fast path.)
What is the purpose of the // < in the ToCharArray method?
@corsiKa Looks like some kind of weird artifact after something was scrubbed. I think there's a highly upvoted question from many months (years?) ago that discussed Microsoft running their code through some kind of censor filter that messed some things up. (Like it censored "race" in "race condition.")
@TheodorosChatzigiannakis I added some more info for completeness.
2

ToCharArray method:

ToCharArray method to extract the characters in a string to a character array. It then displays the original string and the elements in the array.

using System;

public class Example
{
   public static void Main()
   {
      String s = "AaBbCcDd";
      var chars = s.ToCharArray();
      Console.WriteLine("Original string: {0}", s);
      Console.WriteLine("Character array:");
      for (int ctr = 0; ctr < chars.Length; ctr++)
         Console.WriteLine("   {0}: {1}", ctr, chars[ctr]);
   }
}

ToArray method :

ToArray method of the List class that act on ranges.

using System;
using System.Collections.Generic;

public class Example
{
    public static void Main()
    {
        string[] input = { "Brachiosaurus", 
                           "Amargasaurus", 
                           "Mamenchisaurus" };

        List<string> dinosaurs = new List<string>(input);

        Console.WriteLine("\nCapacity: {0}", dinosaurs.Capacity);

        Console.WriteLine();
        foreach( string dinosaur in dinosaurs )
        {
            Console.WriteLine(dinosaur);
        }

        Console.WriteLine("\nAddRange(dinosaurs)");
        dinosaurs.AddRange(dinosaurs);

        Console.WriteLine();
        foreach( string dinosaur in dinosaurs )
        {
            Console.WriteLine(dinosaur);
        }

        Console.WriteLine("\nRemoveRange(2, 2)");
        dinosaurs.RemoveRange(2, 2);

        Console.WriteLine();
        foreach( string dinosaur in dinosaurs )
        {
            Console.WriteLine(dinosaur);
        }

        input = new string[] { "Tyrannosaurus", 
                               "Deinonychus", 
                               "Velociraptor"};

        Console.WriteLine("\nInsertRange(3, input)");
        dinosaurs.InsertRange(3, input);

        Console.WriteLine();
        foreach( string dinosaur in dinosaurs )
        {
            Console.WriteLine(dinosaur);
        }

        Console.WriteLine("\noutput = dinosaurs.GetRange(2, 3).ToArray()");
        string[] output = dinosaurs.GetRange(2, 3).ToArray();

        Console.WriteLine();
        foreach( string dinosaur in output )
        {
            Console.WriteLine(dinosaur);
        }
    }
}

Comments

0

If we are talking about converting strings to letters, the ToCharArray () function is faster than ToArray ()

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.