19

In C#, we have this non-static method on the type string:

"abc".ToUpper()

but for char, we need to use a static method:

char.ToUpper('a')

When introducing c# to beginners, they always expect to be able to write the following:

'a'.ToUpper()

Does anyone have insights as why it was designed like this?

The only thing I can think of is performance but then I would have expected a static ToUpper() for the type string too.

  • 7
    My suspicion is that it's simply that whoever designed the one method didn't work with whoever created the other. – Casey Jul 24 '16 at 04:55
  • 1
    @Khanh TO: char is a value type and can't be null unless it's a Nullable, which this question is *not* about. And strings *can* be null, yet string.ToUpper() is an instance method and ToUpper() on a null string results in an exception, so... – BoltClock Jul 24 '16 at 05:01
  • 2
    Objection - call for speculation :) – sara Jul 24 '16 at 09:09
  • 5
    Char is a value type and therefore immutable, such a method cannot work. It could be written as a method that returns the upper-cased char, like String.ToUpper(). But you would have paid for a boxing conversion in .NET 1.x, the most obvious reason why it wasn't considered back then. You can easily add it today with an extension method. – Hans Passant Jul 24 '16 at 12:06
  • 1
    Well, actually Strings are immutable too. Also, there is no performance difference between a static call or an object member in this context (they are both immutable objects). – Milton Hernandez Aug 01 '16 at 21:22
  • 4
    This is just speculation, but I think the answer is a lot less interesting: .Net 1.x was heavily based on Java, and in Java primitives are not Objects, therefore for a 'char' value, you just don't have the option of calling a method, as in 'a'.toUpperCase(), so in Java they had from the beginning a static method style for primitives ( Character.toUpperCase('a') ) and a member method for String, which is an Object ( myStr.toUpperCase() ). My guess is, C# just followed the same convention. – Milton Hernandez Aug 01 '16 at 21:27
  • @HansPassant Strictly speaking value types could be mutable, but of course I totally agree such bad form would likely not be found in the BCL. I'm not sure what you mean by *it could be written as a method that returns the upper-cased char* - that's actually exactly what it does, which is also why the mutability comment seems irrelevant. Finally I'm not sure where we have boxing here, or why anything would have change in that regard since .NET 1.x (as we don't have generics in this case). – Ohad Schneider Aug 09 '16 at 16:53
  • @HansPassant strings are immutable too I'm not sure how that applies –  Jan 02 '20 at 16:40

3 Answers3

2

The difference lies in the fact that string is a reference type, and char is a keyword that represents the .Net Framework's Char Structure. When you call Char.ToUpper('a') you are actually using the Char Structure in C#. Structures are Value Types. Value Types are immutable.

Since structs are immutable, methods that act upon the struct itself do not work as expected (see Why are Mutable Structs Evil). Thus static methods are needed. When calling Char.ToUpper(aChar) you are not actually changing aChar, instead, you are creating a new instance of a character that is the uppercase representation of the character you passed in as a parameter and returning it. The example below demonstrates this.

Char aChar = 'a';
Char.ToUpper(aChar);
//aChar still equals 'a'

Char bChar = 'b';
bChar = Char.ToUpper(bChar);
//bChar now equals 'B'

The reason char has other methods that allow you to do things like 'a'.Equals('a'); is because value types and reference types both inherit from Object, which defines those methods (technically, value types are of type System.ValueType, which inherits from System.Object). These methods do not enact any changes to the object itself.

Edit - Why this question is actually speculation

As I'm very curious to see if there's an actual answer to "why do chars not have a .ToUpper() method", I decided to check out the CSharp 5 Language Specification Document, I have found the following:

char is an Integral Type (pg 80), which is a subset of Simple Types. Simple Types themselves are just predefined Struct Types. Struct types are Value Types that "can declare constants, fields, methods, properties, indexers, operators, instance constructors, static constructors, and nested types" (pg 79).

string is a Class Type, which is a Reference Type (pg 85). Class Types define "a data structure that contains data members (constants and fields), function members (methods, properties, events, indexers, operators, instance constructors, destructors and static constructors), and nested types" (pg 84).

At this point, it is obvious that chars can support a .ToUpper() method (which is why the extension method works). However, as the question states, they do not support one. At this point I'm convinced any reasoning as to why this is true is pure speculation (unless you're on the C# team, of course).

Community
  • 1
  • 1
JoshuaTheMiller
  • 2,582
  • 25
  • 27
  • String are immutable too so I'm not sure why it matters here. It was never about expecting 'a'.ToUpper() to mutate the value type, just to return the uppercase value. – Francois Beaussier Aug 10 '16 at 00:01
  • @FrancoisBeaussier Good point. I ended up looking into the specifications of C# 5, which made me reach the same conclusion as sara (from the comment section of your question). See my edit for further explanation. – JoshuaTheMiller Aug 10 '16 at 16:44
1

Hans Passant mentioned that it is possible to achieve this syntax easily via extension methods. I'll provide the code here in case anyone is deeply attached to using that syntax.

public static class MyExtensionMethods
{
    public static char ToUpper( this char c )
    {
        return char.ToUpper( c );
    }
}

Then you can do:

'a'.ToUpper()
Wyck
  • 10,311
  • 6
  • 39
  • 60
  • 2
    This doesn't answer the question that was asked. The question wants to know *why* not *here is how you do it* – Isaac Zais Aug 09 '16 at 18:21
1

(sorry, not enough space in a comment -- I know this isn't a complete answer.)

This seems to be a pattern across all the primitive types; int, double, and bool, for example, also do not have methods (except ToString() variants). So it's not just char -- it's a property of all the primitive types that c# defines.

I would guess (and it is a guess) that any time you access data, you're either directly accessing bits of RAM -- the primitive values like int and char and byte -- or you're accessing a .NET construct like an object or struct. A char is always 2 bytes at a particular memory address. So the framework can treat it like a raw memory location.

If we try to treat the raw RAM as objects, you'll either have to 'box' everything to do any work, or it's just not possible. My guess is that you can't do some core feature like virtual method dispatch on primitives, and that the world of objects and the world of primitives has to be kept separate.

Anyway, hope that advances the conversation on some level...

Community
  • 1
  • 1
Steve Cooper
  • 20,542
  • 15
  • 71
  • 88