Istraživanje poboljšanja jezika C# 7.2 i 7.3 za kupce knjige
Namenjeno kupcima knjige C# 7. 1 i . NET Core 2. 0 – Moderno međuplatformsko programiranje - Treće izdanje
LINK KA KNJIZI
With the C# 7 generation, Microsoft has decided to increase the cadence of language releases, releasing minor version numbers, aka point releases, for the first time since C# 1. 1. This allows new features to be used by programmers faster than ever before, but the policy poses a challenge to writers of books about C#.
Introduction
One of the hardest parts of writing for technology is deciding when to stop chasing the latest changes and adding new content.
Back in March 2017, I was reviewing the final drafts of the second edition of my book, C# 7 and . NET Core – Modern Cross-Platform Development. In Chapter 2, Speaking C# I got to the topic of representing number literals. One of the improvements in C# 7 is the ability to use the underscore character as a digit separator.
For example, when writing large numbers in decimal you can improve the readability of number literals using underscores, and you can express binary or hexadecimal number literals by prefixing the number literal with 0b or 0x, as shown in the following code:
// C# 6 and earlier
int decimalNotation = 2000000; // 2 million
// C# 7 and 7. 1
int decimalNotation = 2_000_000; // 2 million
int binaryNotation = 0b0001_1110_1000_0100_1000_0000; // 2 million
int hexadecimalNotation = 0x001E_8480; // 2 million
But in the final draft I hadn't included code examples of using underscores in number literals. At the last minute, I decided to add the preceding examples to the book. Unfortunately, I assumed that the underscore could be used to separate the prefixes 0b and 0x from the digits, and did not check the code examples would compile until the following day, after the book had gone to print. I had to release an erratum on the book's web page before it even reached the shelves. I felt so embarrassed.
In the third edition, C# 7. 1 and . NET Core 2. 0 – Modern Cross-Platform Development, I fixed the code examples by removing the unsupported underscores after the prefixes since they are not supported in C# 7 or C# 7. 1. Ironically, just as the third edition was due to go to print, Microsoft released C# 7. 2, which adds support for using an underscore after the prefixes, as shown in the following code:
// C# 7. 2 and later
int binaryNotation = 0b_0001_1110_1000_0100_1000_0000; // 2 million
int hexadecimalNotation = 0x_001E_8480; // 2 million
Gah! Clearly, I wasn't the only programmer who thought it is natural to be able to use underscores after the 0b or 0x prefixes.
For the third edition, I decided not to make any last-minute changes to the book. This was partly because I didn't want to risk making a mistake again, and also because the code examples do work, they just don't show the latest improvement. Maybe in the fourth edition I will finally get the whole book perfect! But, of course, in the programming world that's impossible.
Since the third edition covers C# 7. 1, I have written this article to cover the improvements in C# 7. 2 that are available today, and to preview the improvements coming early in 2018 with C# 7. 3.
Enabling C# 7 point releases
Developer tools like Visual Studio 2017, Visual Studio Code, and the dotnet command line interface assume that you want to use the C# 7. 0 language compiler by default.
To use the improvements in a C# point release like 7. 1 or 7. 2, you must add a configuration element to the project file, as shown in the following markup:
<LangVersion>7. 2</LangVersion>
Potential values for the markup are shown in the following table:
LangVersion
Description
7, 7. 1, 7. 2, 7. 3, 8
Entering a specific version number will use that compiler if it has been installed.
default
Uses the highest major number without a minor number, for example, 7 in 2017 and 8 later in 2018.
latest
Uses the highest major and highest minor number, for example, 7. 2 in 2017, 7. 3 early in 2018, 8 later in 2018.
To be able to use C# 7. 2, either install Visual Studio 2017 version 15. 5 on Windows, or install . NET Core SDK 2. 1. 2 on Windows, macOS, or Linux from the following link:
https://www. microsoft. com/net/download/
Run the . NET Core SDK installer, as shown in the following screenshot:
Setting up a project for exploring C# 7. 2 improvements
In Visual Studio 2017 version 15. 5 or later, create a new Console App (. NET Core) project named ExploringCS72 in a solution named Bonus, as shown in the following screenshot:
You can download the projects created in this article from the Packt website or from the following GitHub repository:
https://github. com/PacktPublishing/CSharp-7. 1-and-. NET-Core-2. 0-Modern-Cross-Platform-Development-Third-Edition/tree/master/BonusSectionCode/Bonus
In Visual Studio Code, create a new folder named Bonus with a subfolder named ExploringCS72. Open the ExploringCS72 folder. Navigate to View | Integrated Terminal, and enter the following command:
dotnet new console
In either Visual Studio 2017 or Visual Studio Code, edit the ExploringCS72. csproj file, and add the element, as shown highlighted in the following markup:
<Project Sdk="Microsoft. NET. Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2. 0</TargetFramework>
<LangVersion>7. 2</LangVersion>
</PropertyGroup>
</Project>
Edit the Program. cs file, as shown in the following code:
using static System. Console;
namespace ExploringCS72
{
class Program
{
static void Main(string[] args)
{
int year = 0b_0000_0111_1011_0100;
WriteLine($"I was born in {year}. ");
}
}
}
In Visual Studio 2017, navigate to Debug | Start Without Debugging, or press Ctrl + F5.
In Visual Studio Code, in Integrated Terminal, enter the following command:
dotnet run
You should see the following output, which confirms that you have successfully enabled C# 7. 2 for this project:
I was born in 1972.
In Visual Studio Code, note that the C# extension version 1. 13. 1 (released on November 13, 2017) has not been updated to recognize the improvements in C# 7. 2. You will see red squiggle compile errors in the editor even though the code will compile and run without problems, as shown in the following screenshot:
Controlling access to type members with modifiers
When you define a type like a class with members like fields, you control where those members can be accessed by applying modifiers like public and private.
Until C# 7. 2, there have been five combinations access modifier keywords. C# 7. 2 adds a sixth combination, as shown in the last row of the following table:
Access modifier
Description
private
Member is accessible inside the type only. This is the default if no keyword is applied to a member.
internal
Member is accessible inside the type, or any type that is in the same assembly.
protected
Member is accessible inside the type, or any type that inherits from the type.
public
Member is accessible everywhere.
internal protected
Member is accessible inside the type, or any type that is in the same assembly, or any type that inherits from the type.
Equivalent to internal_OR_protected.
private protected
Member is accessible inside the type, or any type that inherits from the type and is in the same assembly.
Equivalent to internal_AND_protected.
Setting up a . NET Standard class library to explore access modifiers
In Visual Studio 2017 version 15. 5 or later, add a new Class Library (. NET Standard) project named ExploringCS72Lib to the current solution, as shown in the following screenshot:
In Visual Studio Code, create a new subfolder in the Bonus folder named ExploringCS72Lib. Open the ExploringCS72Lib folder. Navigate to View | Integrated Terminal, and enter the following command:
dotnet new classlib
Open the Bonus folder so that you can work with both projects.
In either Visual Studio 2017 or Visual Studio Code, edit the ExploringCS72Lib. csproj file, and add the element, as shown highlighted in the following markup:
<Project Sdk="Microsoft. NET. Sdk">
<PropertyGroup>
<TargetFramework>netstandard2. 0</TargetFramework>
<LangVersion>7. 2</LangVersion>
</PropertyGroup>
</Project>
In the class library, rename the class file from Class1 to AccessModifiers, and edit the class, as shown in the following code:
using static System. Console;
namespace ExploringCS72
{
public class AccessModifiers
{
private int InTypeOnly;
internal int InSameAssembly;
protected int InDerivedType;
internal protected int InSameAssemblyOrDerivedType;
private protected int InSameAssemblyAndDerivedType; // C# 7. 2
public int Everywhere;
public void ReadFields()
{
WriteLine("Inside the same type:");
WriteLine(InTypeOnly);
WriteLine(InSameAssembly);
WriteLine(InDerivedType);
WriteLine(InSameAssemblyOrDerivedType);
WriteLine(InSameAssemblyAndDerivedType);
WriteLine(Everywhere);
}
}
public class DerivedInSameAssembly : AccessModifiers
{
public void ReadFieldsInDerivedType()
{
WriteLine("Inside a derived type in same assembly:");
//WriteLine(InTypeOnly); // is not visible
WriteLine(InSameAssembly);
WriteLine(InDerivedType);
WriteLine(InSameAssemblyOrDerivedType);
WriteLine(InSameAssemblyAndDerivedType);
WriteLine(Everywhere);
}
}
}
Edit the ExploringCS72. csproj file, and add the element to reference the class library in the console app, as shown highlighted in the following markup:
<Project Sdk="Microsoft. NET. Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2. 0</TargetFramework>
<LangVersion>7. 2</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include=". . \ExploringCS72Lib\ExploringCS72Lib. csproj" />
</ItemGroup>
</Project>
Edit the Program. cs file, as shown in the following code:
using static System. Console;
namespace ExploringCS72
{
class Program
{
static void Main(string[] args)
{
int year = 0b_0000_0111_1011_0100;
WriteLine($"I was born in {year}. ");
}
public void ReadFieldsInType()
{
WriteLine("Inside a type in different assembly:");
var am = new AccessModifiers();
WriteLine(am. Everywhere);
}
}
public class DerivedInDifferentAssembly : AccessModifiers
{
public void ReadFieldsInDerivedType()
{
WriteLine("Inside a derived type in different assembly:");
WriteLine(InDerivedType);
WriteLine(InSameAssemblyOrDerivedType);
WriteLine(Everywhere);
}
}
}
When entering code that accesses the am variable, note that IntelliSense only shows members that are visible due to access control.
Passing parameters to methods
In the original C# language, parameters had to be passed in the order that they were declared in the method. In C# 4, Microsoft introduced named parameters so that values could be passed in a custom order and even made optional. But if a developer chose to name parameters, all of them had to be named. In C# 7. 2, you can mix named and unnamed parameters, as long as they are passed in the correct position.
In Program. cs, add a static method, as shown in the following code:
public static void PassingParameters(string name, int year)
{
WriteLine($"{name} was born in {year}. ");
}
In the Main method, add the following statement:
PassingParameters(name: "Bob", 1945);
Visual Studio Code will show an error, as shown in the following screenshot, but the code will compile and execute.
Optimizing performance with value types
The fourth and final feature of C# 7. 2 is working with value types while using reference semantics. This can improve performance in very specialized scenarios. You are unlikely to use them much in your own code, unless like Microsoft themselves, you create frameworks for other programmers to build upon that need to do a lot of memory management. You can learn more about these features at the following link:
https://docs. microsoft. com/en-gb/dotnet/csharp/reference-semantics-with-value-types
Conclusion
I plan to refresh this bonus article when C# 7. 3 is released to update it with the new features in that point release. Good luck with all your C# adventures!