Check out a free preview of the full C# and .NET Basics course
The "Class Constructor" Lesson is part of the full, C# and .NET Basics course featured in this preview video. Here's what you'd learn in this lesson:
Spencer demonstrates how constructors are used to instantiate objects and set initial values for their properties. He also discusses the use of default values, multiple constructors, object initializer syntax, and validating data in constructors. He briefly mentions the concept of referential equality and how it can be overridden using the equals method or interfaces.
Transcript from the "Class Constructor" Lesson
[00:00:00]
>> Spencer Schneidenbach: So constructors are really important part of classes, so lots of classes use constructors to kind of gatekeep and say, hey, I can not instantiate. I cannot become a physical object in the .NET world, unless you provide me specific things that I can use to exist. So let me show you what that looks like, so, if I read declare my person class here like that.
[00:00:27]
>> Spencer Schneidenbach: And I have a name and an age, I'm gonna go ahead and close these setters, in other words, I'm just gonna delete them to kind of prove a point. I've made this object, essentially what's called immutable, you can't change anything about this object, at least not direct, not directly without a little bit of magic.
[00:00:45]
But you can see that I have indeed actually set these values to something else up here, but only in the context of what is called a constructor. Now, a constructor looks a lot like a method, except it doesn't have a return type, and the name of the method is always the name of the class.
[00:01:04]
So it's a little bit of a funky syntax, if you're looking at methods and you're trying to make them into a method, it's a very special, very purpose built type of method and this is how you use it. So if I wanted to create a new person var spencer equals new, so new is a keyword you use to what's called instantiate or create an instance of this class.
[00:01:27]
We were materializing spencer into our digital world, so we do new person and then the syntax for calling this constructor is the same as you would for calling a method. These are still called parameters up here, they can have params arguments, for instance, they can even have default values.
[00:01:46]
You can set age to 37, which happens to be my age, and you can say, I wanna call this constructor, and you can see that age 37. I can call it with just my name, or if I wanna pretend I'm 29 I can do that as well. So that's what's called instantiation, now we've brought this object into existence.
[00:02:08]
It's a reference type, so that means it is materialized in a point in memory, and this variable is referencing that value in that memory. So, classes are really important, because they are, pretty much the fundamental building block for any real programming or business programming inside of the .NET world.
[00:02:27]
They're kind of a container for properties, but they don't always just contain properties, they can also contain behaviors, which is really an important part, and we'll get to that here in a second.
>> Student 1: So maybe I'm not understanding, or maybe this is the point when you're creating the constructor and setting age to 37 and then you can only get there's no way to set it.
[00:02:52]
How is it that when you are then creating or instantiating spencer and 20 and setting the age to 29 that you can do that?
>> Spencer Schneidenbach: Absolutely, so that's a good question and it really has to do with default values. Default values are about ultimately like providing flexibility to the programmer, especially the lazy programmer.
[00:03:13]
You may decide that, what, I just want to instantiate this person object with an age of 37 or I don't want that default value. I wanna, override it, so what would happen is, is once this is instantiated and we do this, I'm gonna debug this and I'll show you the value.
[00:03:31]
>> Spencer Schneidenbach: You can see that the value is now overridden to 29, because I have made it so.
>> Student 1: Okay, so it's just setting it to 37, initially as just the default value?
>> Spencer Schneidenbach: Correct, and that makes it optional, right? So you can, if you want, remove this argument, and then once we hit a debug and we go back here, you'll see that it is now indeed 37.
[00:03:55]
So it's, there's default values are used quite a bit in programming and, in the real world for lots of different things, for methods, for constructors. So they do have their they have a lot of purpose, so good question, you bet. Okay it's important to note that a class can actually contain multiple constructors.
[00:04:20]
Now you can see here that, in a sense, we have one constructor, but it's got some flexibility. We can call it multiple different ways, right? We can call Amy, Amy is 24, we can call this constructor two different ways with two different sets of arguments, because it's got a default value.
[00:04:42]
But this isn't just the only way you can instantiate a classic, class can have what's called multiple constructors. You can have multiple. Ways of instantiating a class, and this is really important to note because it's frankly very common. It's very common when you're talking about objects that serve the purpose simply of processing data, that there may be some different ways to process it.
[00:05:10]
And sometimes it needs things and sometimes it doesn't need things, right? So multiple constructors is really about giving the developer flexibility, you may be able to create an object, multiple different ways, and just give that developer some flexibility. So you can see I've removed the constructor with the default value, so if I go back here and I type in my parens, you can see now that it's saying, well, there's two different ways I can be instantiated.
[00:05:39]
So there's string, dot name, and then there's string dot name and int age.
>> Spencer Schneidenbach: This gets really important when you're talking about service-based classes, and I said, we will talk about this. We'll probably touch on this in the next course, in the ASP.NET Core course, but just know that multiple constructors can exist on a class to construct them in different ways.
[00:06:02]
>> Spencer Schneidenbach: You can also save yourself some keystrokes by simply instantiating or simply calling a constructor within another constructor. And the way that you would do that is afterwards, you see that you can use right after your constructor declaration, you can do this, and then you can do a colon, and then you can type this.
[00:06:26]
And this allows you to then call other constructors, so you can say, here I want to pass in this name parameter to this, and then I just wanna set the age to 0. So that's a way of calling a constructor, you can also do the converse, right? You could have this always set the name like so, and then call this and just pass this name so that it always calls this constructor as well, so both are valid, yes Mark.
[00:06:57]
>> Mark: Is it bad practice to do this? Dot, name equals name and, inside of the constructor.
>> Spencer Schneidenbach: Is it bad practice? It is not, and, in fact, a lot of people would consider it to be good practice. So the question really is about accessing name. So let's say, for the sake of argument, and most of the time if you follow the c sharp styling rules, this isn't gonna be a problem.
[00:07:18]
But let's say you have a, string name as a parameter, and parameters are typically, camel case, so the first character is always lowercase. You can have, this is perfectly valid, c sharp, but what it the compiler's doing is, telling you assignment made to the same variable. Did you mean to assign something else?
[00:07:40]
That's because inside of the scope of this constructor, you've actually changed what name looks like, sometimes collision is not allowed in c sharp other times it is. And this is one of the cases where it is, you can absolutely have a parameter declared on a method that is also declared somewhere else in that scope.
[00:07:59]
So the way that you would mitigate that based on Mark's question is to type this in front of that. So that actually refers to the person.name property. So this refers to the the object itself. That's a good question, good way to kinda segue into this keyword. But in practice, you're almost never going to declare a property and then have a name and a parameter that has the exact same name.
[00:08:30]
They're gonna be cased differently. And most of the time, Spencer, because of that, is cuz he's following those style rules. He's gonna bypass using the this keyword. But some people really like the this keyword. Again, it's just referring to itself. Some people really like it because it's really explicit.
[00:08:51]
And even the .NET tooling will try to tell you, hey, you can actually make this simpler, you don't need this keyword here. Great question.
>> Spencer Schneidenbach: All right, I wanna go over the syntax, but I don't wanna dwell on it, because, frankly, I don't use it a lot and I don't like it.
[00:09:13]
[LAUGH] This is one of those features of C# that's relatively new that I think it fits fine into the language, but it's not something that I am personally a fan of. You can do what's called a primary constructor. And a primary constructor allows you to set, basically in the same line with this parentheses up here, right after the class declaration.
[00:09:36]
In the same line, you can set string Name and int Age, and that will actually automatically create properties for you as well. So if we go down to amy., no it does not create properties for you. Let me see, am I missing some syntax here? Let's see. Create and assign property name.
[00:09:58]
Okay, that's not true for record types. Okay, I misspoke there. I'm actually gonna rewind and start that over. So you can use what's called a primary constructor, which you can declare basically a parentheses, and then give it some values up here. And this is a constructor definition. I'm a fan of using multiple construction.
[00:10:20]
And oftentimes if I'm declaring an object with a constructor, there's a good chance that it'll have multiple constructors that might have multiple different purposes. So I'm not a fan of this syntax, but it will play well with multiple constructors as well. It's just that I personally don't like it.
[00:10:40]
Now, I am gonna show you a little bit of the tooling. You're gonna see that Parameter 'Name' is unread. You see that it's asking me to do a quick fix. So I'm going to hit Cmd + Period, and you're gonna see that it will give you some suggestions, it will give you some code hints, or some hints of things that you can do.
[00:10:56]
So in this case, I am going to declare a property called Name and then set it to the Name parameter here. Again, I am not a fan of primary constructors, but I have seen them in the wild because .NET developers like new features. So there you go. There's the syntax for it.
[00:11:14]
I don't just particularly care for the syntax, so a lot of times, I just don't use it.
>> Spencer Schneidenbach: So we talked about the new keyword to create instances of a class. We should talk about as well validating data that comes into a class. So I'm gonna unwind all of this, and I am gonna go back to the this.Name.
[00:11:39]
That's a fine syntax to go from. And I am going to show you a very common pattern when you're constructing objects, which is to check to see that the parameters of your object are valid. So I'm gonna go here, and I'm going to say, if the string is null or whitespace, I'm going to throw an ArgumentException to say that Name cannot be null or empty.
[00:12:00]
You can also give it in the ArgumentException, you can give it the name of the parameter if you want. That's useful for somebody who's handling that exception downstream. But this basically will prevent our object from being created in an invalid state. So I'm gonna run this, and I'm gonna show you that it's gonna crash at runtime.
[00:12:23]
Boom, let's see. Unhandled exception, Name cannot be null or empty, (Parameter 'name'). So it threw our system to ArgumentException, and we did that at the constructor, and we did that deliberately, right? We don't want an invalid representation of an object to exist in memory if we can help it.
[00:12:39]
This renders this object completely unusable. So you can't use it and it is not instantiated. And you cannot recover from this except to catch the exception and try to re-instantiate the object.
>> Spencer Schneidenbach: Okay, this is one of my favorite pieces of syntax, and I think it's a really cool one.
[00:13:00]
You've probably seen it already. You've probably seen me use it already, which is that, I'm gonna go ahead and put this as a setter. I'm gonna open up the setter here. Oftentimes when I'm dealing with objects that are, quote, unquote, representations of real world things like a person, I'm not usually using constructors.
[00:13:19]
I use constructors for other things, which I'll touch on more in the ASP NET Core course. But for representing objects like people, I'm not using a constructor directly. But there may be a situation where I want to instantiate this object with certain values. So you can use what's called object initializer syntax.
[00:13:38]
So let me show you what that looks like. So I'm gonna say new Person. I can leave off the open and close parentheses if I want, but I'm gonna put them on there just to be explicit. And then I'm gonna use this syntax after. I'm gonna have this little tiny block.
[00:13:53]
And this block, if I hit Ctrl + Space to kinda see what I've got of available to me, I can see that I can set age and name. And what I can do is I can set these properties as I'm instantiating this object. The syntax was introduced a long time ago.
[00:14:13]
It is a syntax that I use a lot to declare objects, because the alternative is to just do new Person and then person.Name, and then person.Age. And oftentimes the tooling will tell you, hey, there's an easier way to do this.
>> Spencer Schneidenbach: So it'll say object initialization can be simplified.
[00:14:35]
Ctrl + Dot, and you'll see, it'll suggest to do that. Super common syntax, super nice, saves you key strokes, and I think it looks nicer, too. This is a relatively new thing. You can also declare a property as immutable, but set it inside of the object initialization syntax only.
[00:14:54]
Such that you can replace the setter with an init, and that basically says the way that you set this is within the object initializer only, and then you're not able to set it after the fact. So it effectively converts it to be immutable. This becomes important when you talk about nullability, because as you can see, we've got this little squiggly line, it's probably been bothering some of you.
[00:15:18]
And it says non-nullable property 'Name' must contain a non-null value when exiting constructor. And this is just a compiler warning saying, hey you have nullability turned on, so we're gonna give you these extra compiler hints, because you haven't declared this name with a question mark. You haven't said that this is a nullable object.
[00:15:34]
So it should have null or it should not be null. So you can do what's called required. This is really relatively new in the last version or two of C# as of the date of this course. And you can basically say, you need to instantiate this in the object initializer or else we're gonna throw a compiler error.
[00:15:54]
It'll say required member 'Person.Name; must be set in the object initializer. So the point of me showing you all this is to really just show you that there's a lot of different ways that you can kind of declare classes. I think the most important ones you need to know are the git set right here, which just creates a plain old property.
[00:16:16]
I don't use git init a lot, I have not used git in it required a lot either. And the main reason is that I'm working on older code bases a lot of the times, even just a few years old before nullability really started to pick up steam. These are really important when you're talking about newer projects, but for older projects, it's almost impossible to go back and add nullability in a meaningful way to the whole project.
[00:16:40]
If it's of any complex size, is simply because, hey, we're programmers. They're paying us to ship features, not to turn things from nullable to not so most of the time, that just gets bypassed. For newer projects, I would probably tend to use git init and git required in it, I'd probably use them a lot more.
[00:17:01]
I want to touch on one last thing, which is that reference type, reference types have what's called reference referential equality. That means that even though these references, these persons, these person classes instantiated, have the exact same name and the exact same age as each other. If we try to print console, dot write line, if we print person one equals person two, it's going to print false.
[00:17:36]
And the reason is, is that they are different references in memory, so they're different things. You can compare them, you can write your own custom method to do so, you can override the object equals. So we'll talk about overriding when we talk about abstract classes essentially allows you to change behavior of existing methods.
[00:17:56]
You can add in an equalizers or and you can override the equal method such that you're able to tell the person class that, yes, these things are equal, but that's because we've explicitly set it to do so. That one didn't work, and I think maybe because hash code isn't set, but anyways, we'll talk about, I think it's actually person1.equalsperson2, that's the one, okay.
[00:18:24]
I used the wrong comparison, I was using a double equals to compare as opposed to the actual equals method. Which is to say that equality in C-Sharp has some nuance to it such that a person1 double equals person2 doesn't have the same behavior as person1.equals using this method.
[00:18:49]
You can actually if you want to override the equals operator, you can actually say, I want the doubles equal operator to have this very specific behavior. In practice I almost never do that, simply because if I'm checking the equality of an object like this, I'm probably doing it in a very explicit way and not hide and I'm not usually hiding that behind the language feature.
[00:19:11]
Because then the expectation is, is that, well, if I can do double equals on person, why can't I do it on animals as well? And if you don't have it implemented on animals, now you're inconsistent in your code base. Lastly, you can implement an interface, which we will talk about interfaces very shortly.
[00:19:28]
You can implement an interface that gives you a way to define an equals method for other classes, so you can check it to see you can define an equal method for checking to see if it's equal to another person. You could also do it if the person class is different from an employee class, you can implement this interface.
[00:19:50]
I just want you to know that this exists for now, obviously, we haven't covered interfaces yet. Just know that this is a thing that exists, when looking at interfaces and you look back and refer to this, you'll see and you'll be able to see what it's used for.
[00:20:06]
But for most of the purposes of things right now, I will tell you that I almost never implement this interface, I almost never write code like this. Again, I'm usually checking a quality in a very like explicit way, usually just I'll define a custom, are these people equal method or something along those lines?
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops