Why is my `char` argument in an extension method interpreted as `int`?

Viewed 61

I wrote a .DLL library with a very simple extension method for string.

My method - .Remove(char deletedChar), removes all occurrences of a char in a string. This overloads the default .Remove(int startIndex) method. However, when I want to use it, .Remove(int startIndex) is invoked, instead of my method, despite me giving char as the argument.

What I mean is, given this code:

string Test = "12-34-56-78-90-123-456-789-0123-4567-89012-34567-890123-456789-000000";
MessageBox.Show(Test.Remove('-'));

My expected result is:

1234567890123456789012345678901234567890123456789000000

However, the actual result is:

12-34-56-78-90-123-456-789-0123-4567-89012-34

Check it out on the screenshot:

ss

This means, that my char '-' is interpreted as its ASCII value (45), and this is the start index for removal of the string.

Why does this happen? Even casting char to a char (i.e. (char)'-') does not fix it. I know I can simply rename the extension method, but I still cannot understand why does this happen. Could someone explain this phenomenon or point to a doc that explains it?

Pasting my extension method in case someone will want to use it:

    /// <summary>
    /// Removes all occurences of specified char.
    /// </summary>
    /// <param name="str"></param>
    /// <param name="deletedChar">The char you want to remove.</param>
    /// <returns>string without the specified char.</returns>
    public static string Remove(this string str, char deletedChar)
    {
        for (int i = 0; i < str.Length; i++)
        {
            if (str[i] == deletedChar)
            {
                str = str.Remove(i, 1);
                i--;
            }
        }
        return str;
    }
1 Answers

C# instances methods are called before extensions if found.

From the documentation:

When the compiler can't find an instance method with a matching signature, it will bind to a matching extension method if one exists.

Writing this helper class, we can see that the good method is called:

public static class StringHelper
{
  public static string MyRemove(this string str, int index)
  {
    return "remove at index";
  }
  public static string MyRemove(this string str, char code)
  {
    return "remove all chars";
  }
}

Test

Console.WriteLine("".MyRemove(1));
Console.WriteLine("".MyRemove('a'));

Output

remove at index
remove all chars

Thus the solution is to use a dedicated method's name:

public static class StringHelper
{
  public static string RemoveAll(this string str, char deletedChar)
  {
    for ( int i = 0; i < str.Length; i++ )
    {
      if ( str[i] == deletedChar )
      {
        str = str.Remove(i, 1);
        i--;
      }
    }
    return str;
  }
}

Therefore the design and the usage is more clear and clean, as well as more more speaking while the type of the parameter tells what to remove.

But such a method is not optimized and can for example be replaced by:

public static string RemoveAll(this string str, char code)
{
  return str.Replace(code.ToString(), "");
}

Or by this one which is better:

using System.Text;

public static string RemoveAll(this string str, char code)
{
  var builder = new StringBuilder();
  foreach ( char c in str )
    if ( c != code )
      builder.Append(c);
  return builder.ToString();
}

Or using Linq, but probably less optimized than the previous:

using System.Linq;

public static string RemoveAll(this string str, char code)
{
  return new string(str.Where(c => c != code).ToArray());
}
Related