0

I'm trying to create an extension method to simplify a small bit of code that I use a lot:

var test = "this is a test.";
var substring = test.Substring(0, 4);
test = test.Remove(0, 4).Trim();

After running the aforementioned code snippet and printing the resulting values for test and substring to a console window, you'll see:

this
is a test.

I'd like to simplify it with an extension method which would be an effective overload of Substring:

public static string Substring(this string input, int startingIndex, int length, bool removeFromInput = false) {
    var substring = input.Substring(startingIndex, length);
    if (removeFromInput)
        input = input.Remove(startingIndex, length);

    return substring;
}

This would allow my code going forward to be simplified to:

var test = "this is a test.";
var firstWord = test.Substring(0, 4, removeFromInput: true).Trim();
var secondWord = test.Substring(0, 2, removeFromInput: true).Trim();
var thirdWord = test.Substring(0, 1, removeFromInput: true).Trim();
var lastWord = test.Substring(0, 4, removeFromInput: true).Trim();

Let's not get pedantic and focus on the fact that I could just split on whitespace here; this isn't a universal example, and more often than not it's not focused on words but rather particular substrings.

After looking around the web for a bit, I understand why this doesn't work for value types (here is a post on that), but string is a reference type, though it's treated as a value type in many cases (here is a post on the topic). Is this another one of those cases? If so, why?


Why can't I update the value of a string in an extension method?

I'm not looking for alternatives, I'm trying to understand why this doesn't work since string is a reference type. My alternative implementation is test.Substring(0, 4, removeFrom: ref test) which I think still reads well.

Hazel へいぜる
  • 2,751
  • 1
  • 12
  • 44
  • `public static string Substring(ref string input,` usage `Helper.Substring(ref test,0, 4, true).Trim()` You cant make this an extension method because of the ref. – TheGeneral Oct 19 '21 at 21:43
  • @TheGeneral I understand that I can do that; I'm trying to understand why I can't do it the way I'm presenting. – Hazel へいぜる Oct 19 '21 at 21:44
  • 2
    A string is *always* an **immutable reference type**. (Except for hacks to modify the underlying memory.. in which case it is still *never a value type*.) – user2864740 Oct 19 '21 at 21:45
  • 1
    Compiler says no. `¯\_(ツ)_/¯` "*The first parameter of a 'ref' extension method ... must be a value type or a generic type constrained to struct.*" seems fairly straight forward. read more here https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods#common-usage-patterns – TheGeneral Oct 19 '21 at 21:45
  • @user2864740 that's probably what I've been missing TBH. – Hazel へいぜる Oct 19 '21 at 21:45
  • Even if it was possible, it would not be a good idea as other peoples that read the code won't expect that behavior as none of string member function modify the string but return a copy. **Code readability** is more important that daving a few keystrokes and you can always create a code snippet. A good reason to work that way is exactly because it make the code much more easier to understand as you know that none of the member function modify `this`. Much of the potential confusion is avoided that way as there is a single rule to remember. – Phil1970 Oct 19 '21 at 23:09

1 Answers1

2

The problem here is that although the string is a reference type, when you pass a parameter to a method that reference is copied. So a new variable is created that points to the same object in memory. When you make that variable point to a new object, it doesn't affect the original object.

Here is an example to clarify what I mean:

var foo = "foo";
var bar = foo;

bar = "bar";

We create a string and store it's reference in the foo variable. Then we create a bar variable and store the reference of foo in it. This is a copy of the reference, now 2 variables are pointing to the same object.

When we create a new string and assign it to bar, it doesn't affect foo. Now the bar just holds a reference to a different object.

There is a way to make it so that the original parameter is modified and reference is not copied, and it can be done by using ref parameters. But as far as I know ref modifier is not supported for the first parameter of extension methods.

Selman Genç
  • 100,147
  • 13
  • 119
  • 184