【问题标题】:Is string actually an array of chars or does it just have an indexer?字符串实际上是一个字符数组还是只有一个索引器?
【发布时间】:2010-09-08 15:14:02
【问题描述】:

由于以下代码在 C# 中是可能的,我很想知道字符串是否实际上是一个字符数组:

string a="TEST";
char C=a[0]; // will be T

【问题讨论】:

  • 我猜这将取决于实现。用反射器查看字符串类的内部
  • 最好不要打扰字符串的内部表示方式。 Microsoft 的实现可能与 Mono 的不同,后者可能与 Compact Framework 的不同......针对接口而不是内部的程序:)

标签: c# arrays string


【解决方案1】:

System.String 不是 Char 的 .NET 数组,因为:

char[] testArray = "test".ToCharArray();

testArray[0] = 'T';

会编译,但是这个:

string testString = "test";

testString[0] = 'T';

不会。字符数组是可变的,字符串不是。此外,string is Array 返回 false,而 char[] is Array 返回 true。

【讨论】:

    【解决方案2】:

    不,它不是一个数组。但它确实有一个索引器。两全其美。

    【讨论】:

    • 有弹性,像松紧带一样。
    • 我的意思是你不必用固定大小声明它。
    • 虽然它们确实有固定的尺寸。无法调整字符串的大小,因此它们对我来说似乎没有那么有弹性。
    • "string",严格来说,是字符“向量”的语法糖。它是一个对象,用于存储对以空字符结尾的字符集开头的引用。字符串的实例,即内存中的字符集,是不可变的;看起来您更改字符串(追加、大写、替换等)实际上会导致创建字符串的新实例并将其引用放入字符串中,并且旧字符串被 GCed。这就是为什么像 StringBuilder 这样的构造(在构建字符串时将数据保持在更可变的状态)是一种很好的做法。
    • @KeithS 很好的评论,但我无能为力.. 严格来说,C# 字符串不是向量(至少在 C++ 意义上),也不是空终止的。
    【解决方案3】:

    .NET 中的字符串由 System.String 类提供支持,该类在内部使用一组不安全的方法使用标准 C 内存操作技术对实际字符串数据进行指针操作。

    String 类本身不包含数组,但它有一个 indexer 属性,可让您将数据视为数组。

    【讨论】:

      【解决方案4】:

      为 Scott Dorman 和 Gufa 的回答添加一点内容。如果你在字符串 abcd 上使用 Windbg!DumpObject,你会得到类似这样的东西。

      0:000> !do 01139b24
      Name: System.String
      MethodTable: 79330a00
      EEClass: 790ed64c
      Size: 26(0x1a) bytes
       (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
      String: abcd
      Fields:
            MT    Field   Offset                 Type VT     Attr    Value Name
      79332c4c  4000096        4         System.Int32  1 instance        5 m_arrayLength
      79332c4c  4000097        8         System.Int32  1 instance        4 m_stringLength
      793316e0  4000098        c          System.Char  1 instance       61 m_firstChar
      79330a00  4000099       10        System.String  0   shared   static Empty
          >> Domain:Value  00181b38:01131198 <<
      79331630  400009a       14        System.Char[]  0   shared   static WhitespaceChars
          >> Domain:Value  00181b38:011318b8 <<
      

      您会注意到它只有三个实例字段。 m_arrayLengthm_stringLengthm_firstChar。它不包含实例System.Char[]。其他 2 个字段是静态共享的,因此每个 System.String 具有相同的 Empty 字符串和 WhitespaceChars char 数组。

      如果你跟着 DumpByte 你会看到堆中的字符串数据(在本例中为abcd),当然从偏移量0x0cm_firstChar)开始8 字节宽m_stringLength 4 x 2 用于 unicode)。

      0:000> db 01139b24 L1A
      
      01139b24  00 0a 33 79 05 00 00 00-04 00 00 00 61 00 62 00  ..3y........a.b.
      01139b34  63 00 64 00 00 00 00 00-00 00                    c.d......
      

      如果您查看 SSCLI,您会发现,正如 Scott 所说,它要么运行不安全的代码,要么使用指针技术通过 m_firstCharm_stringLength 读取数据。

      【讨论】:

        【解决方案5】:

        不,String 是 .Net 中的一个类。它可能由数组支持。但它不是一个数组。类可以有索引器,这就是 String 所做的。

        有关此声明的详细说明,请参见 cmets: 据我了解,所有字符串都存储在一个公共 blob 中。因此,“foo”和“foo”指向该 blob 中的同一点......字符串在 C# 中不可变的原因之一。

        【讨论】:

        • 你不应该依赖这样一个事实,即等效字符串是相同的引用,因为我认为一般来说不能保证。
        • 叫字符串实习
        • 同意。永远不要依赖内部实现细节。他们始终保留更改的权利。我添加了该注释,因为 OP 似乎有兴趣了解 String 的工作原理。
        • @Brian。我的理解是,并非所有字符串都被保留。如果它们是 string.IsInterned() 可能不存在。因此,也许您的答案应该是“某些字符串存储在一个公共 blob 中。因此,“foo”和“foo”可能指向该 blob 中的同一点......
        • @Brain 文章说文字是实习的。并非所有字符串都是文字。例如 StringBuilder().Append("wx").Append("yz").ToString();
        【解决方案6】:

        String 是一个类,它需要一个 char 数组来初始化自身,因此当您尝试获取某个索引处的元素时,它会返回 char。 检查字符串类

        public sealed class String : IComparable, ICloneable, IConvertible, IComparable<string>, IEnumerable<char>, IEnumerable, IEquatable<string>
            {
                // Summary:
                //     Initializes a new instance of the System.String class to the value indicated
                //     by an array of Unicode characters.
                //
                // Parameters:
                //   value:
                //     An array of Unicode characters.
                [SecuritySafeCritical]
                public String(char[] value);
            }
        

        另见字符串类声明。

        public sealed class String : IComparable, ICloneable, IConvertible, IComparable<string>, IEnumerable<char>, IEnumerable, IEquatable<string>
        

        IEnumerable&lt;char&gt;继承。

        在 string 类中有一个 get 属性,它在传递索引时返回 char,见图。其中明确表示在当前 System.String 中的指定位置获取 System.Char 对象

        public char this[int index] { get; }
        

        【讨论】:

        • 请不要用包含图片的代码来回答问题。查看您的答案,如果您将这些代码块正确复制到答案中,它应该可以完美运行。
        • @AndrasDeak 感谢您的反馈,我已经编辑了我的答案。
        【解决方案7】:

        string 不是 char[],尽管它确实有 .ToCharArray()。它也有一个索引器,它允许你单独访问字符,就像你展示的那样。它很可能是在内部使用数组实现的,但这是一个实现细节。

        【讨论】:

          【解决方案8】:

          字符串对象包含一个连续的字符块,就像一个字符数组,但字符串对象既不是,也不包含数组对象。

          编译器知道字符串 string 是不可变的,因此它可以在访问字符串时进行某些优化,就像访问数组时进行优化一样。因此,当您通过索引访问字符串时,很可能代码最终会直接访问字符串数据,而不是调用索引器属性。

          【讨论】:

            【解决方案9】:

            字符串根本不是一个数组,因为"Hello" is char[] 被评估为false

            【讨论】:

              【解决方案10】:

              string 不是 chars 的数组,除非您将其转换为一个。该符号仅用于访问字符串中不同位置(索引)的字符。

              【讨论】:

                【解决方案11】:

                使用Reflector,我们可以看到字符串确实实现了IEnumerable&lt;char&gt;。所以,它不是字符数组,但本质上可以像一个一样使用。

                public sealed class String : IComparable, ICloneable, IConvertible, IComparable<string>, IEnumerable<char>, IEnumerable, IEquatable<string>
                

                编辑:

                实现IEnumerable&lt;char&gt;并不意味着该类型会被索引。我不是有意传达的。这意味着您可以枚举它并像集合一样使用它。更好的措辞我的意思是字符串不是字符数组,而是字符的集合。感谢您的评论。

                【讨论】:

                • 这有点不准确。 IEnumerable&lt;char&gt; 不允许索引器用于字符串。如果您需要从任何IEnumerable&lt;T&gt; 访问元素,则必须使用ElementAt 方法;仅仅通过实现IEnumerable&lt;T&gt;,其他对象类型(T)将无法使用索引器。
                【解决方案12】:

                每个人都给出了一半的答案,所以这里是两个部分:

                1) 严格来说,是的,.NET 中的 String 是一个字符数组。它的内部实现和数组的语义定义都是如此。

                2) 然而,正如其他人所指出的,String 有点奇怪。它不是 System.Array 和所有其他数组一样。因此,在严格的 .NET 特定方式中,字符串不是数组。

                【讨论】:

                  猜你喜欢
                  • 2011-06-12
                  • 2015-07-10
                  • 2013-05-03
                  • 2012-05-04
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-07-20
                  • 2015-07-10
                  • 1970-01-01
                  相关资源
                  最近更新 更多