【问题标题】:Difference between ToCharArray and ToArrayToCharArray 和 ToArray 之间的区别
【发布时间】:2016-09-05 11:06:18
【问题描述】:

ToCharArrayToArray有什么区别

string mystring = "abcdef";

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

结果似乎是一样的。

【问题讨论】:

  • ToCharArray() 和 ToArray() 是泛型方法,在其他类中会产生不同的结果。在字符串类中,它们产生相同的结果。字符和字符串是两个字节的实体,但其他类不是两个字节。因此,当您有一个单字节数组时,ToCharArray() 会将数据放入目标的每隔一个字节,而 ToArray() 会将结果放入每个字节。

标签: c# arrays string


【解决方案1】:

string.ToCharArray() 是字符串类的成员。

string.ToArray()实际上是使用a ToArray() extension of IEnumerable<T>,利用string实现IEnumerable<char>这一事实。

在这两者中,string.ToCharArray() 的性能可能更高。

来自C#参考源码,string.ToCharArray()的实现是:

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;
}

同样来自C#参考源,IEnumerable&lt;T&gt;.ToArray()的实现是:

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;
    }
}        

如您所见,这要复杂得多!

IEnumerable&lt;T&gt;.ToArray() 为什么不使用优化路径?

还有一件事我们需要解释。

如果您检查 Buffer&lt;T&gt; 的实现,您会看到以下优化:

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

你可以合理地问为什么不走这条路?如果是这样,这将是对string.ToArray() 的一个很好的优化。

嗯,答案很简单:string 没有实现 ICollection&lt;T&gt;,因此 source as ICollection&lt;TElement&gt; 将返回 null,并且不会进行优化。

更糟糕的是,通过Buffer&lt;T&gt; 的非优化路径将使用string 枚举器,其实现如下:

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);
    }
}    

这引入了另一个层次的低效率。

这个故事的寓意

切勿使用IEnumerable&lt;char&gt;.ToArray() 代替string.ToCharArray()

【讨论】:

  • 如果您解释一下为什么ToArray() 效率会降低,这会更有教育意义:string 没有实现ICollection&lt;char&gt;,这意味着ToArray() 不知道提前的长度,这意味着它将创建很多中间缓冲区。 (否则可以合理地假设 LINQ “足够聪明”以选择快速路径。)
  • // &lt;ToCharArray方法中的作用是什么?
  • @corsiKa 在某物被擦洗后看起来像是某种奇怪的神器。我认为几个月(几年?)前有一个高度赞成的问题讨论了微软通过某种审查过滤器运行他们的代码,这把一些事情搞砸了。 (就像它在“比赛条件”中审查“比赛”一样。)
  • @TheodorosChatzigiannakis 为了完整起见,我添加了更多信息。
【解决方案2】:

ToCharArray 方法:

ToCharArray 方法将字符串中的字符提取到字符数组中。然后显示原始字符串和数组中的元素。

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 方法:

List 类的 ToArray 方法,作用于范围。

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);
        }
    }
}

【讨论】:

    【解决方案3】:

    如果说字符串转字母的话,ToCharArray()函数比ToArray()快

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-05
      • 1970-01-01
      • 2015-04-08
      • 2017-09-03
      • 1970-01-01
      • 2021-12-25
      • 2020-05-10
      相关资源
      最近更新 更多