Lesson 25 - Reference vs. Value Types

Tutorial Series: Introduction to Unity with C# Series

Previous Article  |  Next Article


Transcript

In this video, we're going to cover the difference between value types and reference types. Now I don't want to belabor the point too much but there's an interesting distinction between objects which are value types, reference types, sorry, and simple data types which are value types like bool, int, etc . This distinction becomes a bit more evident when dealing with method input parameters. Now when I say objects, remember I'm referring to variable types created from class definitions which you will be dealing with frequently. Now simply put, the way that objects are handled in memory are quite different from basic data types. Basic data types correlate pretty much one to one with the exact memory registers they are encoded on. Whereas, objects consist of memory registers that point to or refer to various memory registers that in turn make up the object's composition. You'll see the consequence of this value verses reference distinction in the following code. We will write most for our code here in the RPGController.

First let's take all of the previous code we wrote and put it in it's own method, just so we can preserve it but get it out of the way. Call this method, MethodInputsAndReturns() just so you know what it's all about. We can call it here and then immediately comment it out and go on to writing new code. All right so actually we'll do the same thing just so we get ahead of ourselves a bit here and put everything in a new method called ValueVersusReference() to illustrate this distinction. We'll call it here in case we forget, right away. Right away I'll illustrate the way that value types behave so let's create a simple value type called num1 and we'll assign 0, to that and then a second one num2 and we'll assign to that whatever num1 is. Now if we change the value of num2, well on that line, we'll change the value of num2 and then output to the Debug.Log() the contents of num1. So you'll probably be able to anticipate what this will output so let's output it anyways and see if you're correct as to what num1 holds, 0, right? Now let's see what happens when we replicate this process with a reference type, so let's create a new Wizard call this one wizard1 = new Wizard() and let's give it a WeaponType, so WeaponType equals... let's say "Staff "

Alright, now let's create a second Wizard, wizard2 and we'll assign to it wizard1 and now what we'll do is we'll take that wizard2, change its WeaponType. Give that wizard an Orb, right? In the Debug.Log() let's see what wizard1 holds So, try to predict what you think wizard1 will hold. Is it a Staff or is it an Orb? Well that's just see when we run this what happens. Interesting result, well let's break this down then try to make some sense of this. Here we see when the num two ent data type copies num1 because that's essentially what's happening. It's copying is contents it simply makes a copy of the values stored in num1, but doesn't any longer hold a connection or a reference to num1. So that's how we've been accustomed to it behaving so far with just simple value types. Now this is in stark contrast to the wizard1 and wizard2 objects where wizard to simply copies the reference to wizard1 so we do that here, we're copying, wizard2 is copying the reference to wizard1 there, not any contents in wizard1. When wizard1 changes wizard2 changes as well and vice versa so that's why here when we changed wizard2 to Orb it changes that very same reference back to wizard1's WeaponType of Staff to Orb right. The original memory space that both wizard1 and wizard2 point to is actually what changes. The reference both have the same reference to the same memory space so hopefully if you keep that in mind that will make a lot of sense.

With this in mind, what do you think happens when we pass in, let's say a Knight which is clearly a reference type since it's an object and not a simple data type it's just like the Wizard. We pass that into a method as an input parameter and perform an operation on that Knight object within that method. Does the object external to the method retain the changes made within the method to that Knight? Or is the operation being performed on another object local to the scope of the method, thereby only making changes on a copy that doesn't reflect back to the original? We can find the answer by changing a field for the Knight within a method and see if that change is reflected back out to the original Knight object we passed in as an input parameter. Let's code that up right now. Actually, before we do this I want to show you something, this error list and output sometimes it just gets in the way so it's helpful to be able to hide it so there you have this pin here which if you click it, it auto-hides and if you want to bring it up again you just click on the error list here, but as soon as you click off it goes away. So, as soon as you start coding it gets out of the way. I just want to get it out of the way because we're going to end up needing some more real estate here to work with. Here what we'll do is, we'll make reference to the kinght's WeaponType and see if the knight we passed in, when we change the WeaponType of that knight, does that change reflect back to the Knight we input as an input argument on the calling side?

Let's code that up, kight.weapon.WeaponType equals... Now let's just say it's resulting in a broken weapon, let's just have that to make it more interesting. And so basically we'll concatenate "broken" to the beginning of whatever weapon it is that the knight's holding then we reference the knight's WeaponType. On the calling side let's do this let's make a ... let's actually comment this out actually and let's add another comment here, just for posterity. Let's say "Becomes Orb", implying that it's surprising. It won't be after this lesson. And here we will remind ourselves that the value type retains its original value because there's no reference between the two value types there. Now that we've commented all that out, we're going to need a knight. Actually let's first do wizard. Wizard wizard = new Wizard(). And now what we'll do is we'll make a knight and we will say as we did before, equals wizard.weapon.Deflect() and we will input the knight and we have to give it an integer so we will say 30 like before and we don't have to output the defense message any longer so we'll just output to the Debug.Log() Now we're just interested in what happens to the original knight, this one that we pass in as an input argument. Does his WeaponType become broken? Does that become concatenated to his weapon? Which we didn't actually assign so let's do so here. And run it. Broken sword.

So, perhaps you didn't expect that. You may have thought somewhat correctly that the knight input parameter should be local to the method and would copy something passed in from the original Knight. It's actually partly correct but with a twist, what's happening here is we're copying the reference to original Knight object. Something is being copied local to the method, but it happens to be the reference. Even though the method is performing an operation on a copy of sorts, it's a copy that refers back to the original, because it's a reference type. In case that didn't make sense to you do remember that input parameters are basically local variables. Local to the scope of the method itself so when you supply something of value or reference type for those input parameters at the method call either with a literal value or another variable it becomes copied into the local input parameter. Now let's see how this coping has an impact on whether or not you're copying a reference or a value. Let's go back to just using string as input. Let's go back to the deflect method and right now we can safely comment this all out as we don't need it anymore. Keep things simple. And instead of passing in the knight we will go back to just passing in a string to illustrate this point, and we'll take that lower case w. Make sure you don't want to reference the field, reference the WeaponType that's passed in and we'll do much the same as we did with the code we just wrote but now we know we're passing in, instead of a reference, we are passing in a value right. It's just a string it's no longer an object. And on the calling side we'll need to change this from a knight to the knight's string WeaponType to make it work.

I got an error here now because this is no longer a string, this reference is still passing in a knight object, so let's just to get this to work let's pass in a string, alright now let's see the result of that. When we run it we see that, unlike the behavior we saw when we passed in an object reference, simply passing in a value maintains no reference back to the original object. Instead what's being copied is simply the value to an entirely new memory register. Again, with no reference back to the object. This is actually a source of lots of confusion I think to beginning coders because it's really Easy to make the mistake of passing in a field that belongs to an object such as we did in this case, which is not a reference type, it's just a simple value type and expecting it to change the reference in the object as if you passed in the object, right? The analogy I would use for understanding reference types versus value types, it's kind of like when someone gives you on the internet a copy of a document to edit on your computer so you have a local copy. You two are, you and the other person are just editing two different local copies, so therein lies a problem. What if you guys both want to see what each other have done and merge those changes? Well on the internet we have a solution to this problem, we have sharing from a common document source on the cloud for example through something like Dropbox.

A reference type would be something like you and that other person sharing a document both locally pointing to that version on the cloud, that one actual document source. Each of you have a reference that is like a handle that's holding on to the original document versus if you are both working on your own local documents and then have to find a way to merge the changes together. Whatever changes you make on your computer are different from what every changes the other person makes on their computer, that's more like a value type. From now on you will often hear me say value when referring to basic data types and object when referring to reference types. That's pretty much all you need to know about that. It's not a problem that ends up popping up too often, but when it does it could cause for a sticky situation, especially when you're inputting it an object as an input parameter verses a value as we've seen here. Alright so that's about it for that. And I'll see you in the next lesson. Thanks.


Related Articles in this Tutorial:

Lesson 1 - Who This Course is For

Lesson 2 - What to Expect from this Course

Lesson 3 - Installation and Getting Started

Lesson 4 - Starting the First Project

Lesson 5 - Prototype Workflow

Lesson 6 - Basic Code Review

Lesson 7 - Game Loop Primer

Lesson 8 - Prototyping Continued

Lesson 9 - C# Fundamentals and Hello World

Lesson 10 - Variables and Operations

Lesson 11 - Variables and Operations Continued

Lesson 12 - Floats, Bools and Casting

Lesson 13 - If Statement Conditionals

Lesson 14 - If Statements Continued

Lesson 15 - Complex Evaluations and States

Lesson 16 - Code Syntax vs. Style

Lesson 17 - Variable Scope

Lesson 18 - Object-Oriented Programming Intro

Lesson 19 - OOP, Access Modifiers, Instantiation

Lesson 20 - Object Containment and Method Returns

Lesson 21 - "Has-A" Object Containment

Lesson 22 - "Is-A" Inheritance Containment

Lesson 23 - Static Fields and Methods

Lesson 24 - Method Inputs and Returns

Lesson 25 - Reference vs. Value Types

Lesson 26 - Introduction to Polymorphism

Lesson 27 - Navigating the Unity API

Lesson 28 - Applying What You've Learned and Refactoring

Lesson 29 - Constructors, Local Variables in the Update Method

Lesson 30 - Collecting Collectibles, Items and Powerups

Lesson 31 - Spawning and Managing Prefab Powerups

Lesson 32 - Implementing Powerup State Logic

Lesson 33 - Displaying Text, OnGUI, Method Overloading

Lesson 34 - Referencing Instantiated GameObjects, Parenting

Lesson 35 - Understanding the Lerp Method

Lesson 36 - Creating Pseudo Animations in Code

Lesson 37 - Understanding Generic Classes and Methods

Lesson 38 - Animations Using SpriteSheets and Animator

Lesson 39 - Working with Arrays and Loops

Lesson 40 - Debugging Unity Projects with Visual Studio

Lesson 41 - Camera Movement and LateUpdate

Lesson 42 - Playing Audio Clips

Lesson 43 - Routing Audio, Mixers and Effects

Lesson 44 - Adding Scoring Mechanics and Enhancements

Lesson 45 - Scene Loading and Game Over Manager

Lesson 46 - Understanding Properties

Lesson 47 - Controller Mapping and Input Manager

Lesson 48 - Understanding Enums

Lesson 49 - Dealing with Null References

Lesson 50 - Handling Variable Framerates with time.DeltaTime

Lesson 51 - Preparing the Project for Final Build

Lesson 52 - Final Build and Project Settings

Lesson 53 - Introduction to the Unity Physics Engine

Lesson 54 - Understanding FixedUpdate vs. Update

Lesson 55 - Movement Using Physics

Lesson 56 - Attack Script and Collision Events with OnCollisionEnter2D

Lesson 57 - Projectiles and Stomping Attack

Lesson 58 - Parallax Background and Scrolling Camera

Lesson 59 - Infinitely Tiling Background Sprites

Lesson 60 - OOP Enemy Classes

Lesson 61 - OOP Enemy Classes Continued

Lesson 62 - Trigger Colliders and Causing Damage

Lesson 63 - Multi-Dimensional Arrays and Procedural Platforms

Lesson 64 - Finishing Touches

Lesson 65 - Series Wrap


Comments

Please login or register to add a comment