Why do some Methods not require a specified class? (Extremely basic question)

Cshape65d

New member
Joined
Jan 11, 2022
Messages
4
Programming Experience
Beginner
I apologize for such a basic question, but I'm extremely new to C# and can't seem to confidently answer this question:

Why does something like:
string myStringVariable2 = myStringVariable.ToUpper()
not require me to call the String Class?

But something like:
int myIntVariable2 = Math.Abs(myIntVariable)
does require the Math class call-out.

Is ToUpper an "extension" variable and this is why it's not necessary?
Are all methods that can be called with just a period and the method name attached to your variable considered "extension" variables?
When I look up the ToUpper method in the String class, it looks like it's built like other methods so I don't understand why it's not written the same way when referenced. I was expecting to see the use of "this" as a parameter but don't see it.

Please note: any guidance the terms/phrases/terminology used in this question would be greatly appreciated. Part of not being able to find an answer on my owns comes to my very limited and likely inaccurate C# vocabulary.

Thank you very much
 
Last edited:
This is an example of why you should ALWAYS read the documentation. Here's the declaration of the String.ToUpper method:
C#:
public string ToUpper ();
and here's the declaration of the Math.Abs method:
C#:
public static int Abs (int value);
Spot the difference? If you then research the static keyword, you'll have your answer.
 
Yes, it's very important to fully understand the difference between class variables/methods (which are static) and instance variables/methods (which are not).
But it's confusing sometimes... In this case for example. Why did they implement myString.toUpper() but not myInt.Abs() ? It seems a bit arbitrary to me.
 
Class methods
These methods operate on the internal properties of a class, and are only available for use after a class has been initialized. In this context; string is a class, and ToUpper is a method that accesses the internal stored string property value of the class and uses it to compute its upper case equivalent, which it then returns this as a new instance of a string.

Side note:
Class methods can never be pure in the mathematical sense as its quite normal to never specify any of the inputs, that the method can directly access within the initialized instance of the class. More so it is also normal for some class method to never specify a return value; a void method call, can as example alter the state of the class instance. i.e. it's a computed action on the internal state of the class, and returns no value.

Static methods
These are also termed a function; functions do not have access to any internal properties; their inputs are typically specified in the static method's signature. In the example of Abs; it's input is a single int property, named value.

Side note:
A static method that only computes a result based on defined inputs and always returns a value is referred to as a pure function; pure in the mathematical sense; because it guarantees referential transparency.

Note:
In languages like C# and Java; the design of the language has stipulated that everything needs to be defined in a class; which is why a static method (function) must exist within a class; the difference is that the class does not need to be initialized to use the static method; in this case you can think of the class as a type of folder of sorts, a folder that contains Math functions. e.g. the Math "folder" class contains all the Math related static methods (functions).
 
Last edited:
Why did they implement myString.toUpper() but not myInt.Abs() ? It seems a bit arbitrary to me.
Math methods work on several numeric data types, but not all of them, and they don't have a common type.
 
Thanks, that explains !
Also int is an integral type and not a class type like string; so they originally. did not have any dot chained methods that you'd typically find for a class type like string; however since the addition of extension methods you can add this yourself to any type including integral types like int, for example:
C#:
namespace .. {
  public static class MathExtensionMethods {
    public static int Abs(this int a) => Math.Abs(a);
    public static decimal Abs(this decimal a) => Math.Abs(a);
  }
  
  public class Program {
     static public void Main() {
        var abs1 = -1.Abs(); // abs1 == 1
        var abs2 = -19.69M.Abs(); // abs2 == 19.69M
     }
  }
}
 
Also int is an integral type and not a class type like string; so they originally. did not have any dot chained methods that you'd typically find for a class type like string; however since the addition of extension methods you can add this yourself to any type including integral types like int, for example:
C#:
namespace .. {
  public static class MathExtensionMethods {
    public static int Abs(this int a) => Math.Abs(a);
    public static decimal Abs(this decimal a) => Math.Abs(a);
  }
 
  public class Program {
     static public void Main() {
        var abs1 = -1.Abs(); // abs1 == 1
        var abs2 = -19.69M.Abs(); // abs2 == 19.69M
     }
  }
}
Having previously been exposed to Java, I find that C# has somewhat blurred the lines between class and integral type. I mostly like that, as it's a lot more pragmatic, but in cases like this one evidently needs to be aware of the differences.

Your above code is very interesting, containing some new stuff for me to learn. Thanks for that ! So I added these lines at the end:
Added lines:
Console.WriteLine("abs1 = " + abs1);
Console.WriteLine("abs2 = " + abs2);
and compiled and executed the code. And to my dismay the output is
Console output:
abs1 = -1
abs2 = -19.69

Not what we expected ! However this code
Modified code:
            var abs1 = -1;
            var abs2 = -19.69M;

            abs1 = abs1.Abs();
            abs2 = abs2.Abs();

            Console.WriteLine("abs1 = " + abs1);
            Console.WriteLine("abs2 = " + abs2);
produces the expected output:
Expected output:
abs1 = 1
abs2 = 19.69
Seems like you cannot apply these functions directly on a constant like you suggested. Or am I missing something ?
 
Last edited:
This works:
C#:
var abs2 = (-19.69M).Abs();
Without the paranthesis the - is applied afterwards.
If you do this:
C#:
var test = -14.ToString("x");
Compiler complains:
Operator '-' cannot be applied to operand of type 'string'
 
Having previously been exposed to Java, I find that C# has somewhat blurred the lines between class and integral type. I mostly like that, as it's a lot more pragmatic, but in cases like this one evidently needs to be aware of the differences.

Your above code is very interesting, containing some new stuff for me to learn. Thanks for that ! So I added these lines at the end:
Added lines:
Console.WriteLine("abs1 = " + abs1);
Console.WriteLine("abs2 = " + abs2);
and compiled and executed the code. And to my dismay the output is
Console output:
abs1 = -1
abs2 = -19.69

Not what we expected ! However this code
Modified code:
            var abs1 = -1;
            var abs2 = -19.69M;

            abs1 = abs1.Abs();
            abs2 = abs2.Abs();

            Console.WriteLine("abs1 = " + abs1);
            Console.WriteLine("abs2 = " + abs2);
produces the expected output:
Expected output:
abs1 = 1
abs2 = 19.69
Seems like you cannot apply these functions directly on a constant like you suggested. Or am I missing something ?
My bad... its an order of precedence problem.

The extension methods essentially do work as I indicated; however the order of precedence in C# makes it appear as if it doesn't. C#'s order of precedence internal rules stipulate that dot method calls are processed before prefix operators, so the extension method Abs, will only see the positive int value, and similarly the positive decimal value.
After the Math.Abs result is computed, C# then computes the - prefix operator... which leaves you scratching your head thinking it didn't work... to overcome the order of precedence problems simply wrap your negative int and negative decimal values in parentheses and that will ensure that C# processes the - operator prefix before it computes the dot method call of Abs.

For example:
C#:
var abs1 = -1.Abs();
var abs2 = -19.69M.Abs();

Console.WriteLine($"abs1 = {abs1}"); // abs1 == -1
Console.WriteLine($"abs1 = {abs2}"); //abs2 == -19.69

Whereas the parentheses instructs C# to first process the - prefix operator before any dot chained methods calls.:
C#:
var abs1 = (-1).Abs();
var abs2 = (-19.69M).Abs();

Console.WriteLine($"abs1 = {abs1}"); //abs1 == 1
Console.WriteLine($"abs1 = {abs2}"); //abs2 == 19.69
 
Last edited:
Awesome info everyone! I really appreciate it. After some additional digging, here's my basic interpretation of it all. Please let me know if any of this is inaccurate, including my terminology:

Class Methods (aka Non-static Methods)
  • Require initialization
  • Initialization of a class method can be done in multiple ways:
    • You can create a new variable (e.g. simply create a new string variable)
    • Or you can create a new object using the "new" keyword (e.g. MyCustomClass myCustomClassObject = new MyCustomeClass;)
    • You can then use either the variable or the object to attach a "dot chained method" or "extension method."
Static Methods (aka Instance Methods)
  • Do not require initialization
  • You can simply call them by including the Class name (e.g. int myIntVariable2 = Math.Abs(myIntVariable))
You can also create a "extension method" based off a static method by using the "this" keyword.

(For what it's worth, I think I did know one was static and one wasn't, I just didn't realize you call them differently 😓 )

Writing it out like this greatly helps my retention. Thanks again for everyone assistance.
 
Last edited:
Some object oriented teachers use the terms class methods vs member methods.
 
Awesome info everyone! I really appreciate it. After some additional digging, here's my basic interpretation of it all. Please let me know if any of this is inaccurate, including my terminology:

Class Methods (aka Non-static Methods)
  • Require initialization
  • Initialization of a class method can be done in multiple ways:
    • You can create a new variable (e.g. simply create a new string variable)
    • Or you can create a new object using the "new" keyword (e.g. MyCustomClass myCustomClassObject = new MyCustomeClass;)
    • You can then use either the variable or the object to attach a "dot chained method" or "extension method."
Class methods / instance methods are by themselves not initialized... because they are an inherent part of the class definition they require the class that contains them to be initialized before they can be used. Normally it's easy to spot an initialization of a class by the use of the new keyword; however that's hardly ever used for a class like string; similarly for classes that are initialized using helper static methods. Irrespective of how they are initialized, the instance methods / class methods cannot be accessed until the class is initialized.

Static Methods (aka Instance Methods)
  • Do not require initialization
  • You can simply call them by including the Class name (e.g. int myIntVariable2 = Math.Abs(myIntVariable))
You can also create a "extension method" based off a static method by using the "this" keyword.

(For what it's worth, I think I did know one was static and one wasn't, I just didn't realize you call them differently 😓 )

Writing it out like this greatly helps my retention. Thanks again for everyone assistance.
Static methods are not instance methods.
It's as I mentioned before; useful to consider that static methods are simply created within a class structure to collectively catalogue it, for example:
  • The mathematic static methods are housed in a class called Math.

Also a class can have both class methods / instance methods and static methods (aka functions) within the same class definition; however the class methods will not be available for use until an instance of the class has been created, whereas the static methods will be available at all times. For example:

C#:
public class Person {
    public int ID;
    public string Lastname;
    // initialiser
    public Person(int id, string lastname) => (ID, Lastname) = (id, lastname);
    // static method (helper method used to initialize an instance of Person)
    public static Person New(int id, string lastname) => new(id, lastname);
    // class nethod
    public override string ToString() => $"ID: {ID}, Lastname: {Lastname}";
}

static public void Main() {
    // Error attempt to access class / instance method before class is initialized
    Console.WriteLine(Person.ToString());

    // No problem to call the static member before the class Person is initialized
    var person1 = Person.New(1, "Jack");  // this returns an intialized instance of Person.

    // Once we have an instance of Person, we then can access the class / instance methods.
    Console.WriteLine(person1.ToString());

    // Initializing and instance of Person using the new keyword
    var person2 = new Person(1, "Jack"); // this has the outcome as calling the static method New.
}

Final note:
Extension methods are static methods, except that they are differentiated from general static methods by the use of the this keyword to prefix a type that they are extending... as dot method extensions; which are superficially indistinguishable from class / instance methods.
The benefit of extension methods is that almost any type can be extended; even the ones that you can't modify because they're not your code, e.g. class types from a 3rd party library.
 
Last edited:
Back
Top Bottom