 In addition to fields, a class can have zero or more methods. What is a method? Well, it's basically just a function. In object-oriented programming, the term method is preferred over function, though really you can use them interchangeably. Some pedants will insist that you only use the term method in C sharp, but a lot of programmers will just loosely say function when they need method. So here in this class cat, we have this method foo, which returns a string and has a single parameter a of type string, and this method bar, which returns nothing and has no parameters. But the special thing about these methods is that implicitly, their first parameter is a special parameter called this, which has the same type as the class which contains the methods. So these methods are inside cat. So their first parameter implicitly is this parameter called this, which is of type cat. And then same thing for bar. It implicitly has this parameter this of type cat. So implicitly, this is how these methods are defined, but we don't actually write this parameter. We just leave an implicit. It exists implicitly. Otherwise, this special parameter is like any other parameter, but for one restriction, normally our parameters are like variables and we can assign to them like we do any other variable inside a method. So we can hear assigned to a just any string, and this would be valid. But we can't ever assign to this. So this in this method will be of type cat. So normally with a cat variable or parameter, we can assign it some cats, but the compiler does allows this. The reason for this restriction is not from some technical limitation. It's just a language trying to force broad notions of how objectory code is supposed to be structured. Otherwise, we can use this parameter like any other. And in this case, especially this parameter will be of type cat. So we can set its name to be some string. And this is a valid operation. When we call foo, the first argument will be some cat. The reference to that cat, the address of the cat will be stored in this. And then we can do whatever we want with it like any other cat reference. And understand nothing actually obligates us to in any way use this special parameter. Here in the bar method, the this parameter exists, it's there. We're just not using it. We're just ignoring it. So again, this line here is a compilation here, I'll get rid of that. So now imagine elsewhere in code in some method somewhere. We want to call the methods of cat. Well, we need a cat instance to call those methods. We'll create a variable C of type cat to assign it a cat instance. Here we're calling bar passing in the reference to the cat, which is assigned to the special this parameter, though that's then ignored in the method. And then here we're calling foo also passing in the cat, which is assigned to the this parameter which is here used in this set operation. And we also pass in the string high, which is assigned to this parameter a for normal reference type parameters, like say the string parameter here, it's perfectly fine to pass in null. When we call the method, like say here, instead of passing the string, if we make this null, this is still a perfectly valid call. That's if we call a method and pass null to the special this parameter, that trigger is an exception a runtime error. So here if we made C null and then try to call bar and foo at runtime, the language that object that this can't be null, it'll trigger an exception on this call. The idea of a method is that it's supposed to be an operation enacted upon instances of the class that contains them. These are the things that we want to do with our cats. So it doesn't really make sense to invoke one of these methods when we don't have an actual cat. And also be clear that these are runtime errors. You might assume that the compiler could look at this and say, well, obviously when we get down here, C is going to have the value null. So the compiler in this case could analyze their code and figure out that, yeah, this is going to be null. So it's going to be a bad call. But it doesn't actually do that. Because in this case, yes, it could figure out that the value of C is going to be null, but in many, many other cases, most of the cases, it couldn't figure out what the value of our variable is going to be. Like say if we just had some condition here, some condition that depended upon user input. And now if we conditionally assign C the value null, when we get down here, the compiler just can't possibly know if C is going to be null or not. Sometimes it will be, sometimes it won't. So the compiler never presumes to know what the actual value of any variable is. It only knows the type, not the actual value. So that's why whether this is null is checked only at runtime, never compile time. Now, when the compiler looks at our method calls, it determines which method is being called, of course, by the name. But it also goes by the type of the first argument. That is what determines which method we're calling. So say if I happen to have some class dog, which also has a method called bar, taking nothing, returning nothing. I was just saying it prints a wolf. Well, the compiler here when we call bar knows that we're invoking the method bar of cat because it knows that this thing here is a cat. So it must be the cat method we're invoking. Whereas if we were to create a variable of type dog, let's just create a new dog instance, and now we were to call bar passing in D. The compiler looks at this and knows that this is a call to bar of dog, not cat, because we're passing in a dog instance as the first argument. So so far, which method being called is always a runtime choice. So whereas in Pigeon, we can only have one function of a particular name. In C sharp, we can have multiple methods of the same name, if they're in separate classes. But in fact, even within one class, we can have multiple methods of the same name, as long as the compiler can distinguish between calls to one method and another. And what makes calls distinguishable is if they have different numbers of parameters or different types of parameters in different orders. So here, if I have a method bar, and it doesn't matter what it returns, it can turn a string or just let's just say nothing. But now if I give it a parameter, a type integer. Well now, we can't return a thing, excuse me, we'll just print out that value. Sure, whatever. Now when I call bar here, it knows which bar I'm talking about. Well, first off, because C is a cat, it's bar within the cat class. And because there's no arguments, it must be this bar. Whereas if I had an int argument, well now the compiler can look and say, it matches up with this bar. So this is the method being called. The fact that both these methods have the name bar really is just incidental. There's no necessary relationship between these two methods. They can do entirely different things. We could open files and read them in here. We could launch missiles from this bar. They can do entirely different things. Language doesn't care. It just needs to be able to distinguish between calls to this bar and calls to this bar based on the parameters. And understand the order can matter too. So here, if I have AI and another parameter be type string, and here, this bar is a, well, it doesn't matter what the names are, just the types. So if I just reverse the order, this is still fine too. Same types of parameters, but in different order. And so the compiler could distinguish between calls to one bar and calls to the other. So now if I do seven and then a string here, it knows it's this one, because this is the one where it's integer and then string rather than the other way around. So this is called overloading. Within a single class, we're defining methods of the same name, distinguished by having different parameter types or least parameter types in different order. Sometimes stylistically, this is a nice thing to have because maybe there's some operation to perform on your cat class, but there are variations, same basic idea, but taking different inputs. So it makes sense to give those methods the same name and distinguish between them just by their types of inputs. Be clear, as so far described at least, every time we call a method, the choice of which method to call is entirely a compile time decision. The compiler looks first and says what type of instances that determines the class. And then within the class, it looks for a method of the same name, with the parameter types matching the arguments. The normal fields of our classes are called instance fields, because every instance of the class has its own copy of that field. Every cat instance we create has its own name, age, and weight. A static field, in contrast, is a field which exists only once for the whole class. And in fact, it exists independent of any of the instances of the class. What it really is, is just a global variable. So here in class cat, we have the static field x of type i, of type integer. And this exists at the start of our program and persists throughout the lifetime of the program. And the way we access it is with the get and set operator specifying the cat type name itself, rather than an instance of cat. So here if we set x cat to three, that's setting this static field to the value three, and if we get x cat, that's retrieving the value of the static field, which again is really just a global variable. The frankly odd and unnecessary thing in my opinion is that we have to put our global variables in one class or another. We can't have a global variable existing outside of any class. We have to create static fields, interclasses, and those are global variables. Our classes can also have static methods. Here a static method named alex, which returns an int and takes in a string. And unlike our instance methods here bar and foo, our instance methods, these static methods do not have the special this parameter. So when I call this method alex, the way I do so, here I'll just do so from this method bar inside dog. The way I do it is I write the name of the method, specify the class name, your cat, and then pass in the arguments, in this case just a string. So what is a static method? Well, it's just a method where we don't have to have an instance of the class. Honestly, much like static fields are really just global variables, these static methods are really just like ordinary functions. The odd thing though is that we have to decide which class to put them in. We can't just have them outside of our classes. We have to put them in one class or another. And then when we call them, we specify the class in which they are contained. And we do that because well, we could also have static methods named alex in any other class. So we have to specify which classes alex were talking about. And in fact, within a class, we can have multiple overloads of our static methods, just like we do with our instance methods. And the compiler will distinguish between them based on their parameter types. Which reminds me, I'm not sure I made this explicit, but just keep in mind, when you overload methods, the compiler will object to get a compilation error if you have two methods where the parameters are the same. Like here, we can't have two methods bar taking the arguments because then the compiler couldn't distinguish a call to this bar versus a call to that bar. So the compiler will make sure that you don't have any two overloads that conflict. Class can also have special methods called constructors. And these are methods that are implicitly called when we use the new operator. Here, this class cat has two constructed methods. Both of them have the implicit this parameter as their first parameter. And here, when we call new cat with no arguments, it matches this constructor, this overload. And inside, we're simply just setting the weight to 12.3. And in the call, the newly instantiated cat is passed into this parameter, we're then setting its weight to 12.3. And then always with the new operator returns is the new instance. So in fact, inside constructors, you can have return statements, but you never specify any value to return. It's just always implicitly returning the new instance. So you never put a value here in your return statements. But of course, we don't need that return statement at the end. It just implicitly returns at the end of the method. And then here in this new operation, we have three arguments 9.8, a float, a string, and an integer. And that matches this constructor signature. So this is the constructor that's called, and we're setting its weight to the weight parameter, the age to the nth parameter, and the name to the string parameter. So effectively, these are the initial values for the weight, name, and age. They'll understand it'd be perfectly fine if these had the same name as the fields they're being assigned to, and these get updated accordingly. So now this is age and name. And be clear in the set operation where we're setting the field of a class, well, it determines what the type of this instance is. In this case, it's gonna be an instance of cat. So it's looking here for the name of one of the fields. There's not an expression. This is not some variable or anything. It's just a name that has to match one of these names here of the fields. But then here, this is just anything which is an expression. In this case, it's a float variable, which happens to have the same name as the field, but that's just coincidence. It's just gonna be any expression that gives us back a float value. And the fact that the name here is the same as the field, the compiler is not confused by that because it knows that when it looks here, it's not looking at variables, it's just looking at the fields of the class. So in fact, the common style with constructors is if when you are setting fields from parameters, you just give the parameters the same name as the field which they're being assigned to. Now, in these constructor examples, I'm not doing anything interesting other than just setting the initial values of certain fields. But understand in these methods, we could do whatever we want. Otherwise, they're really just like any other method. You could launch missiles in these constructors. Though arguably that's a very surprising thing to do inside a constructor. And so probably not something you should do. In general, constructors are really just for setting up our instances with initial values. But do understand that aside from how they get called, these constructors really are just methods. Last thing about constructors here is that if you create a class with no constructors, well then it only has a so-called default constructor, constructor that takes no arguments and does nothing. But as soon as you give your class a constructor, it no longer has that default constructor. So if I were to take away this constructor, because I have this constructor defined, now there's no default constructor. And so this new operation is invalid because we don't have a cat constructor taking no arguments.