Deep C# - Value And Reference |
Written by Mike James | ||||
Monday, 04 October 2021 | ||||
Page 3 of 3
LifetimesIt is often said that an important difference between value and reference types is their life cycle. In fact, both types of variable have exactly the same behavior with respect to when they are created and destroyed. That is, a value or a reference type is destroyed as soon as the variable is clearly no longer accessible, i.e. goes out of scope. This means, for example, that a variable defined in a method is destroyed as soon as the function terminates. It is this behavior that makes local variables truly local to the method or block that they were declared in. Notice that there can be exceptions to this rule such as static variables which aren’t destroyed until the application terminates. However, it is true to say that the vast majority of variables do behave in this way. What is different between value and reference types is what happens to their data when the variable is destroyed. In the case of a value type variable, the variable and its data are one and the same and so when a value type variable is destroyed so is its data. However, a reference type variable only contains a reference to its data and while the variable and the reference it contains is destroyed, the object that it references isn’t. This is the source of the statement that value and reference variables have different lifetimes. They don’t but the data associated with them can have. Obviously we can’t leave unwanted objects on the heap forever and this is where the system garbage collector comes in. This is a service that periodically scans the heap looking for objects that no longer have any references to them. An object with no references to it is clearly no longer required and using this fact the garbage collector eventually gets round to clearing up the heap. Notice that this difference in lifetime is entirely to do with the way that things are stored. The value and reference variables are stored on the stack and this is naturally self-managing in the sense that when a method returns, all of its local variables are destroyed by the adjustment of the stack pointer. Anything stored on the heap has no such natural cleaning process and we have to implement a garbage collection system to determine when they are no longer required and when they should be removed. How and when to tidy the heap is entirely a matter of efficiency. Garbage collect too often and you use up processor power when the is plenty of heap waiting to be used. Garbage collect too little and you risk bringing the application to a halt while the garbage collector has to work overtime freeing up memory by deleting objects and consolidating free space. DefaultsWhat “value” does a variable have if it hasn’t yet been assigned one? This is just a question of default values. For numeric types the default value is zero. For strings and other reference types the answer is a little more complicated. You can assign the default value to any type using: default(type); or, where the compiler can work out the type, just: default; The default operator or literal is very useful when used with generics, see Chapter 9. For a simple value type the default is produced by the default constructor and this is the value produced by a bit pattern of all zeros, in other words, zero cast to the appropriate type. For a reference type the default value is the special value null which means that the variable isn’t referencing any object. Sometimes variables are automatically initialized to their default value and sometimes they aren’t. The rules for automatic initialization of variables seem strange at first. Any local variable in a method is not automatically initialized and if you try and use it you will see a compiler error telling you that you are trying to use an uninitialized variable. However, if the variable is a property of a class then it is initialized, as are the elements of an array. So if you write: int i; Console.WriteLine(i); in a method then you will see a compile-time error message, but if you write; class MyClassA { int i; public void myMethod(T x) { Console.WriteLine(i); everything works as the variable is automatically initialized. The difference comes down to: variables allocated on the heap are initialized and variables allocated on the stack aren’t. The compiler is cleverer than you might think in determining if a variable is initialized or not. Consider the following: Random R = new Random(); int i; if (R.NextDouble() < 0.5) { i = 0; } Console.WriteLine(i); The variable is initialized roughly 50% of time, but the compiler still flags the use of the variable as uninitialized. If you add an else that initializes the variable then everything is fine and the compiler knows that all paths result in an initialized variable. You can spend many a happy hour trying to fool the compiler into thinking that a variable is initialized when it sometimes isn’t! In Chapter but not in this extract
PostludeThe difference between stack and heap storage, local and global scope and reference and value types permeates all of computing. Even languages that do their best to hide the distinction are easier to understand when you know about the foundations. The way data is stored and handled explains so much about modern programming and there is unlikely to be a big change in programming languages until these basics are superseded. There is also a sense in which the reference type is the superior type. If everything is an object then every variable should be a reference type. Deep C#Buy Now From Amazon Chapter List
Extra Material <ASIN:1871962714> <ASIN:B09FTLPTP9> To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.
Comments
or email your comment to: comments@i-programmer.info
|
||||
Last Updated ( Monday, 04 October 2021 ) |