Pages

C# Features Through the History - Version 1.2



The version 1.2 of the C# language came less than one year after the release 1.0, containing minor changes. Because of this, after the description of this new features, I want to add some interesting ones present in the language that were not described in the previous post.


How to use the examples 

The examples are available here. You can open the solution and play around. Just uncomment the code and have fun! 

ForEach Loop Calling Dispose 

In the first version of the language we had a condition that doesn't allow the code inside a foreach loop calling Dispose on an IEnumable (when this IEnumerable implemented IDisposable interface). 

The method GetEnumerator() will return an instance of CustomizedEnumerator:

In this example, we have a class that implements IEnumerator and IDisposable interfaces. At the end of the iterations in this code, the Dispose() method will be called:


Property Declarations

The interesting point here is talking about the process of generation of the intermediate language (IL). This article explains the process of compilation and generation of the Intermediate Language. 

Thing is that why we are not supposed to create an entity (a method, in this case) with the same signature of the generated method by the IL. It'll cause conflict. This problem leads us to a new concept, called naming conventions. Microsoft contains a complete material about naming conventions but, to simplify, consider that methods should never use the snake case convention. In other words, by convention, to define a method like we defined in the example is incorrect.

This is an example of the method without the problem:

If we rename the get and set methods to get_Age() and set_Age(int age), it'll return an exception at compile time:


Change of Attributes

It's was possible to use an attribute of a public class in the constructor argument of an Attribute. It's not possible anymore:

foreach string interaction

We had an improvement of performance, when using foreach statement for strings, to iterate between each character. This version is using string's indexer rather than the enumerator pattern.

----------------------

Now, let's talk about some concepts since that came since the previous version:


Reflection is a resource of the language that can be used for some situations, like:
  • Access attributes present in metadata;
  • Verify instantiating types in an assembly;
  • Building new types directly at runtime;
  • Accessing methods on types created at runtime (late binding);
  • It's a low level approach, so it can be insecure (you can access private methods and properties);
  • You can use for runtime creation/instantiation of objects, as a test strategy;
  • It can be slow because involve types dynamically resolved.
In this example, Type and methods present in System.Assembly namespace will get or set some properties, based on reflection:


This is a way of working with the C# language, without being managed by the Common Language Runtime. If you want to work with operations involving pointers same way that we can work in C++, you should use this technique. Remember that references are used to refer an existing variable in another name whereas pointers are used to store address of variable.

In this example, we are working with pointers and verifying the position in memory where it's stored:


To allow the execution of unsafe code, you should add this tag inside your project.cs:
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>


The checked keyword is used when you want to enable overflow checking for integral-type arithmetic operations and conversions << at design time ?? >>.

On the other hand, uncheck keyword will prevent overflow checking.

The C# code, for default, works in uncheck mode. Because of this, in the first examples, when we add a value greater that the maximum value allowed by a byte type, the value in the variable will reset. Using checked will cause an exception:

Constructors vs Destructors

As we saw before, constructors are methods called when a class is instantiated. Destructors, on the other side, can be used to destroy instances of the class when they are no longer needed. The destructor will be called by the Garbage collector of the .NET Framework so, the programmer will not have control when to call it.

It's not a particular way to "force" the destructor to be called, neither to force the release  of memory. In this example, we implemented the IDisposable interface and called methods of the Garbage Collector. Even that, we should trust the framework to clean the memory:


Using Keyword

Another interesting characteristic of the language is the presence of the using keyword. It can be used to:
  • Defines a scope at the end of which an object will be disposed in an easy way;
  • Creates an alias for a namespace or imports types defined in other namespaces;
  • If an object implements the IDisposable interface, you can consider the use of using;
  • With using keyword, the Dispose() will always being called, even if an exception occurs.
In this code, we can see that the Dispose() method will be called in an implicit way, because StreamReader inherits from TextReader abstract class, and TextReader implements the IDisposable interface:

----------------------

I hope you have enjoyed the journey until now. Have a nice week!

Here is the list of all articles:


Fabio Ono

No comments:

Post a Comment