The .NET Languages
- What's New in C# 6.0 and VB 14
- Language Primer
- Language Features
- Asynchronous Programming
- The .NET Framework
- Summary
Unlocking the productivity promises of the Visual Studio IDE is at the heart of this book. The IDE, of course, also ships from Microsoft in concert with new versions of the .NET languages and Framework. You need to have a solid grasp of programming the Visual Basic or C# language using the .NET Framework to take advantage of everything Visual Studio has to offer. Of course, you may also need to know many other things such as XAML, Hypertext Markup Language (HTML), Cascading Style Sheets (CSS), TypeScript, JavaScript (and related frameworks), C++, F#, and LightSwitch. Today’s developer likely does not code in a single language syntax. However, VB and C# are still at the core of most Visual Studio development.
In this chapter, we set aside the IDE (for the most part) and focus on the foundations of .NET programming in C# and Visual Basic. We start by highlighting new features of the languages for those who are already familiar with C# and VB. We then include a language primer as a review of some basic .NET programming tasks. We then cover some more in-depth programming features, enhancements to C# 6.0 and VB 14, and language-related IDE enhancements. The chapter concludes with an overview and map of the .NET Framework class library.
What’s New in C# 6.0 and VB 14
This section is for developers looking for highlights on what’s new about the C# and Visual Basic languages. For those who need the basics (or a refresher), we suggest you start by reading the “Language Primer” section a little later in this chapter. You can then return here to see what additions exist to the primer.
In general, the language changes are small additions that help you write cleaner code. They simplify coding by eliminating unnecessary, repetitive code. The changes also make the code easier to read and understand.
Null-Conditional Operators
One of the most repetitive tasks you do as a programmer is to check a value for null before you work with it. The code to do this checking is typically all over your application. For example, the following verifies whether properties on an object are null before working with them. (For a more complete discussion of all operators, see the section “Understanding Operators” later in this chapter.)
C#
public bool IsValid() { if (this.Name != null && this.Name.Length > 0 && this.EmpId != null && this.EmpId.Length > 0) { return true; } else { return false; } }
VB
Public Function IsValid() As Boolean If Me.Name IsNot Nothing AndAlso Me.Name.Length > 0 AndAlso Me.EmpId IsNot Nothing AndAlso Me.EmpId.Length > 0 Then Return True Else Return False End If End Function
Both C# 6.0 and VB 14 now allow automatic null checking using the question mark dot operator (?.). This operator tells the compiler to check the information that precedes the operator for null. If a null is found in an If statement for example, the entire check is considered false (without additional items being checked). If no null is found, then do the work of the dot (.) to check the value. The code from earlier can now be written as follows:
C#
public bool IsValid() { if (this.Name?.Length > 0 && this.EmpId?.Length > 0) { return true; } else { return false; } }
VB
Public Function IsValid2() As Boolean If Me.Name?.Length > 0 AndAlso Me.EmpId?.Length > 0 Then Return True Else Return False End If End Function
The null-conditional operator cleans up code in other ways. For instance, when you trigger events, you are forced to copy the variable and check for null. This can now be written as a single line. The following code shows both the old way and the new way of writing code to trigger events in C#.
C#
//trigger event, old model { var onSave = OnSave; if (onSave != null) { onSave(this, args); } } //trigger event using null-conditional operator { OnSave?.Invoke(this, args); }
ReadOnly Auto Properties
Auto properties have been a great addition to .NET development. They simplify the old method of coding properties using a local variable and a full implementation of get and set. See “Creating an Automatically Implemented Property” in the later section “Language Features.”
However, up until 2015, auto properties required both a getter and a setter; this makes it hard to use them with immutable data types. The latest release now allows you to create auto properties as read only (with just the get). A read-only backing field is created behind the scenes on your behalf. The following shows an example of a full property, a standard auto property, and the new read-only auto property.
C#
Public class Employee { //full property private string name; public string Name { get { return name; } set { name = value; } } //standard auto property public string Address { get; set; } //read-only auto property public string EmpId { get; } }
VB
Public Class Employee 'full property Private _name As String Public Property Name() As String Get Return _name End Get Set(ByVal value As String) _name = value End Set End Property 'standard auto property Public Property Address As String 'read-only auto property Public ReadOnly Property EmpId As String End Class
Read-only auto properties can be assigned from the constructor. Again, they have a hidden backing field. The compiler knows this field exists and thus allows this assignment. The following shows a constructor inside the Employee class shown above assigning the read-only EmpId property. Notice that, in Visual Basic, the Sub New constructor must be used to assign a read-only property.
C#
public Employee(string id) { EmpId = id; }
VB
Public Sub New(ByVal id As String) EmpId = id End Sub
You can also initiate read-only auto properties at the time of their creation (just like the field that backs them), as shown next. Note that if you were to combine this assignment with the previous constructor code (that initialized the read only property), the object creation would happen first. Thus, the constructor init would take precedence.
C#
public string EmpId { get; } = "NOT ASSIGNED";
VB
Public ReadOnly Property EmpId As String = "NOT ASSIGNED"
NameOf Expression
You now have access to the names of your code elements, such as variables and parameters. The .NET languages use the NameOf expression to enable this feature.
Prior to 2015, you often had to indicate the name of a program element by enclosing it in a string. However, if the name of that code element changed, you had an error lurking in your code (unless you managed to remember to change the string value). For example, consider the following code that throws an instance of ArgumentNullException. This class takes a string as the name of the argument. It then uses the string value to find your program element; it’s not strongly typed programming at all.
C#
public void SaveFeedback(string feedback) { if (feedback == null) { //without nameOf throw new ArgumentNullException("feedback"); } }
VB
Public Sub SaveFeedback(ByVal feedback As String) If feedback Is Nothing Then 'without nameOf Throw New ArgumentNullException("feedback") End If End Sub
The NameOf expression eliminates this issue. You can use the expression along with your actual, scoped code element to pass the name of your code element as a string. However, NameOf uses the actual type to reference the name. Therefore, you get compile-time checking and rename support. The following shows an example of throwing the same exception as used earlier but using NameOf.
C#
throw new ArgumentNullException(nameof(feedback));
VB
Throw New ArgumentNullException(NameOf(feedback))
Using (Imports) Statics
The using statement (Imports in VB) allows developers to declare namespaces that are in scope; thus, classes in the namespace do not need to be fully qualified inside your code. (See “Organizing Your Code with Namespaces” later in this chapter.) You can now use the same statement with static classes. To do so, in C# you must include the static keyword as in “using static.” In Visual Basic, you simply use Imports and then specific the static library.
The ability to indicate using (Imports in VB) with a static class tells the compiler that the class and its members are now in scope. This allows you to call a method of the static class without referencing the namespace or even the class name inside your code.
As an example, consider the static class System.Math. You could add a using statement to the top of your code file. In that case, calls to the static methods would no longer need to be qualified by namespace and class. Instead, you could call the method directly. The following shows the difference between the two approaches.
C#
using static System.Math; ... //use the static method, round without using return System.Math.Round(bonus, 0); //use the static method return Round(bonus, 0);
VB
Imports System.Math ... 'use the static method, round without imports Return System.Math.Round(bonus, 0) 'use the static method Return Round(bonus, 0)
String Interpolation
The .NET languages allow you to replace portions of a string with values. To do so, you use String.Format or StringBuilder.AppendFormat. These methods allow you to use placeholders as numbers inside curly braces. These numbers are replaced in series by the values that follow. This is cumbersome to write and can lead to confusion.
In 2015, the code editor allows you to put the variable right in the middle of the string. You do so using the format that starts the string with a dollar sign ($) as an escape character. You can then add curly braces within the string to reference variables, as in {value}. The editor gives you IntelliSense for your values, too. The call to String.Format then happens for you behind the scenes. The example that follows shows how the previous use of String.Format is now simplified with enhanced string literals.
C#
//old style of String.Format return String.Format("Name: {0}, Id: {1}", this.Name, this.EmpId); //string interpolation style return ($"Name: {this.Name}, Id: {this.EmpId}");
VB
'old style of String.Format Return String.Format("Name: {0}, Id: {1}", Me.Name, Me.EmpId) 'string interpolation style Return $"Name: {Name}, Id: {EmpId}"
Lambda Expressions as Methods (C# Only)
Methods, properties, and other bits of code can now be assigned using lambda expression syntax (in C# only). (See “Write Simple Unnamed Functions Within Your Code (Lambda Expressions)” later in this chapter.) This makes writing and reading code much easier. The following shows a full method implementation as a lambda and a single expression. Notice that we use the string interpolation discussed in the prior section.
C#
public override string ToString() => $"Name: {this.Name}, Id: {this.EmpId}";
Index Initializers (C# Only)
Prior language editions brought developers the concept of creating an object and initializing its values at the same time. (See “Object Initializers” later in this chapter.) However, you could not initialize objects that used indexes. Instead, you had to add one value after another, making your code repetitive and hard to read. C# 6.0 supports index initializers. The following shows an example of creating a Dictionary<string, DateTime> object of key/value pairs and initializing values at the same time.
C#
var holidays = new Dictionary<string, DateTime> { { "New Years", new DateTime(2015, 1, 1) }, { "Independence Day", new DateTime(2015, 7, 4) } };