0

I was learning C# and when I got to the section of Properties, I learned about the one-liner format (ex. public int Property { get; set; }).

After that, I got curious about the String class's Length property and its implementation. When I look at the documentation, it simply reads

public int Length { get; }

even though I would expect a for loop within get to count the characters.

From what I was able to gather, you don't always need a separate member variable (in this case: private int length) when defining a property, but I might expect some internal process counting the characters, storing them in length and then returning that value when get is called from myString.Length. Is there some lower level process occurring when the length of the string is counted or am I not seeing something obvious?

kirito23
  • 21
  • 3
  • 2
    The docs only provide you the definition, not the actual source code with the implementation – Jonesopolis Sep 01 '22 at 19:11
  • 1
    This property is set upon creation of the string and it is also heavily optimized by the compitler.... even inlined. – Jonathan Alfaro Sep 01 '22 at 19:12
  • 1
    You might want to look at https://referencesource.microsoft.com/#mscorlib/system/string.cs,959 – Progman Sep 01 '22 at 19:12
  • Please *do not* include text unrelated to the question in the post (I rolled back invalid edit). See - https://meta.stackoverflow.com/questions/357436/why-isnt-it-required-to-provide-comments-feedback-for-downvotes-and-why-are-pr, https://meta.stackoverflow.com/questions/255644/should-edit-in-edits-be-discouraged, https://meta.stackoverflow.com/questions/260776/should-i-remove-fluff-like-greetings-signatures-thanks-etc-when-editin – Alexei Levenkov Sep 01 '22 at 19:30
  • Just an FYI wrt "_you don't always need a separate member variable_". Only if you implement the get/set accessors of a property in such a way that they don't use fields. Auto-implemented properties (those declared as `{get;set;}` or `{get;}`) implement a field that is hidden from view. But you can find it by using reflection. See this dotnetfiddle: https://dotnetfiddle.net/sC4mrZ. Note the property `MyProp`, its hidden backing field `k__BackingField`, and the property's get/set accessors `get_MyProp`/`set_MyProp`. –  Sep 01 '22 at 19:34
  • .NET is open source so you can see the .NET Core implementation here: [String.cs#L754](https://github.com/dotnet/runtime/blob/5e7cfbf086a03d2613e2e390805470063836262b/src/libraries/System.Private.CoreLib/src/System/String.cs#L754). As you can see it is constant time because, as strings are immutable, the length is cached. Note the code comment *The actual code generated for this will be one instruction and will be inlined.* I'm inclined to close this as a duplicate of [What order of time does the .NET System.String.Length property take?](https://stackoverflow.com/q/2836223/3744182), agree? – dbc Sep 01 '22 at 20:45
  • It's worth noting that (I believe) that the fact that `String.Length` is **O(1)** (i.e., there's no calculation involved) is not contractual. I think (but I'm not sure), someone could implement a .NET version that counts characters (`strlen`-style) and that would be fine. What you need to know is that `String.Length` returns the length of the string being referenced – Flydog57 Sep 01 '22 at 21:33

2 Answers2

4

Characters are never counted because all constructors of String class receive the length information one way or another. It's either the length of the literal, or the length of the array passed, or the buffer length explicitly specified.

In fact, it's impossible to count the characters because there's no "null terminator" for .NET strings. Null is another valid character for a string.

Sedat Kapanoglu
  • 46,641
  • 25
  • 114
  • 148
1

The documentation only provides the definition, not the implementation.

Be that as it may, it could have been implemented with a private set; but it's not. The actual up-to-date source code is currently located here:

        // Gets the length of this string
        //
        // This is an intrinsic function so that the JIT can recognise it specially
        // and eliminate checks on character fetches in a loop like:
        //        for(int i = 0; i < str.Length; i++) str[i]
        // The actual code generated for this will be one instruction and will be inlined.
        //
        public int Length
        {
            [Intrinsic]
            get => _stringLength;
        }

So it has a backing field (and the JIT inlines usage of it, but that doesn't matter).

        // These fields map directly onto the fields in an EE StringObject.  See object.h for the layout.
        //
        [NonSerialized]
        private readonly int _stringLength;

        // For empty strings, _firstChar will be '\0', since strings are both null-terminated and length-prefixed.
        // The field is also read-only, however String uses .ctors that C# doesn't recognise as .ctors,
        // so trying to mark the field as 'readonly' causes the compiler to complain.
        [NonSerialized]
        private char _firstChar;

These are the only two fields, but the native version of String is allocated to the full length.

Let's take a look at the StringObject as defined in the native part of the .NET Runtime.

  private:
    DWORD   m_StringLength;
    WCHAR   m_FirstChar;

So it's just an int field that's located before the first character, and it's set on creation of the string, which makes sense: strings are immutable, so it makes little sense to keep recalculating the length.

Charlieface
  • 52,284
  • 6
  • 19
  • 43