Book Image

C# 7 and .NET: Designing Modern Cross-platform Applications

By : Mark J. Price, Ovais Mehboob Ahmed Khan
Book Image

C# 7 and .NET: Designing Modern Cross-platform Applications

By: Mark J. Price, Ovais Mehboob Ahmed Khan

Overview of this book

C# is a widely used programming language, thanks to its easy learning curve, versatility, and support for modern paradigms. The language is used to create desktop apps, background services, web apps, and mobile apps. .NET Core is open source and compatible with Mac OS and Linux. There is no limit to what you can achieve with C# and .NET Core. This Learning Path begins with the basics of C# and object-oriented programming (OOP) and explores features of C#, such as tuples, pattern matching, and out variables. You will understand.NET Standard 2.0 class libraries and ASP.NET Core 2.0, and create professional websites, services, and applications. You will become familiar with mobile app development using Xamarin.Forms and learn to develop high-performing applications by writing optimized code with various profiling techniques. By the end of C# 7 and .NET: Designing Modern Cross-platform Applications, you will have all the knowledge required to build modern, cross-platform apps using C# and .NET. This Learning Path includes content from the following Packt products: • C# 7.1 and .NET Core 2.0 - Modern Cross-Platform Development - Third Edition by Mark J. Price • C# 7 and .NET Core 2.0 High Performance by Ovais Mehboob Ahmed Khan
Table of Contents (25 chapters)
Title Page
Copyright
About Packt
Contributors
Preface
16
Designing Guidelines for .NET Core Application Performance
Index

Casting and converting between types


You will often need to convert between different types. For example, data input is often done into a text field, so it is initially stored in a variable of the stringtype, but it then needs to be converted into a date, or time, or number, or some other data type, depending on how it should be stored and processed.

Casting has two varieties: implicit and explicit. Implicit casting happens automatically and it is safe, meaning that you will not lose any information. Explicit casting must be performed manually because it may lose information, for example, the accuracy of a number. By explicitly casting, you are telling the C# compiler that you understand and accept the risk.

Add a new console application project named CastingConverting.

Casting from numbers to numbers

Implicitly casting an int variable into a double variable is safe.

Casting numbers implicitly

In the Main method, enter the following statements:

int a = 10;
double b = a; // an int can be stored in a double
WriteLine(b);

You cannot implicitly cast a double variable into an int variable because it is potentially unsafe and would lose data.

In the Main method, enter the following statements:

double c = 9.8;
int d = c; // compiler gives an error for this line
WriteLine(d);

In Visual Studio 2017, press Ctrl + W, E to view the Error List, as shown in the following screenshot:

In Visual Studio Code, either view the PROBLEMS window, or enter the dotnet runcommand, which will give the following output:

Compiling Ch03_CastingConverting for .NETCoreApp,Version=v1.1
/usr/local/share/dotnet/dotnet compile-csc
@/Users/markjprice/Code/Chapter03/Ch03_CastingConverting/obj/
Debug/netcoreapp1.1/dotnet-compile.rsp returned Exit Code 1
/Users/markjprice/Code/Chapter03/Ch03_CastingConverting/Program.cs(14
,21): error CS0266: Cannot implicitly convert type 'double' to 'int'.
An explicit conversion exists (are you missing a cast?)
    
Compilation failed.
    0 Warning(s)
    1 Error(s)
    
Time elapsed 00:00:01.0461813

Casting numbers explicitly

You must explicitly cast a double variable into an int variable using a pair of round brackets around the type you want to cast the double type into. The pair of round brackets is the cast operator. Even then, you must beware that the part after the decimal point will be trimmed off without warning.

Modify the assignment statement for the dvariable, as shown in the following code:

double c = 9.8;
int d = (int)c;
WriteLine(d); // d is 9 losing the .8 part

Run the console application and view the output:

10
9

We must perform a similar operation when moving values between larger integers and smaller integers. Again, beware that you might lose information because any value too big will get set to -1!

Enter the following code:

long e = 10;
int f = (int)e;
WriteLine($"e is {e} and f is {f}");
e = long.MaxValue;
f = (int)e;
WriteLine($"e is {e} and f is {f}");

Run the console application and view the output:

e is 10 and f is 10
e is 9223372036854775807 and f is -1

Using the convert type

An alternative to using the casting operator is to use the System.Convert type.

At the top of the Program.cs file, type the following code:

using static System.Convert;

Add the following statements to the bottom of the Main method:

double g = 9.8; 
int h = ToInt32(g); 
WriteLine($"g is {g} and h is {h}"); 

Run the console application and view the output:

g is 9.8 and h is 10

Note

One difference between casting and converting is that converting rounds the double value up to 10 instead of trimming the part after the decimal point.

The System.Convert type can convert to and from all the C# number types as well as Booleans, strings, and date and time values.

Rounding numbers

You have now seen that the cast operator trims the decimal part of a real number and that the convert methods round up or down. However, what is the rule for rounding?

In British primary schools, children are taught to round up if the decimal part is .5 or higher and round down if the decimal part is less.

Enter the following code:

double i = 9.49;
double j = 9.5;
double k = 10.49;
double l = 10.5;
WriteLine($"i is {i}, ToInt(i) is {ToInt32(i)}");
WriteLine($"j is {j}, ToInt(j) is {ToInt32(j)}");
WriteLine($"k is {k}, ToInt(k) is {ToInt32(k)}");
WriteLine($"l is {l}, ToInt(l) is {ToInt32(l)}");

Run the console application and view the output:

i is 9.49, ToInt(i) is 9
j is 9.5, ToInt(j) is 10
k is 10.49, ToInt(k) is 10
l is 10.5, ToInt(l) is 10

Note that the rule for rounding in C# is subtly different. It will round up if the decimal part is .5 or higher and the nondecimal part is odd, but it will round down if the nondecimal part is even. It always rounds down if the decimal part is less than .5.

This rule is known as Banker's Rounding, and it is preferred because it reduces bias. Sadly, other languages such as JavaScript use the primary school rule.

Note

Good Practice For every programming language that you use, check its rounding rules. They may not work the way you expect!

Converting from any type to a string

The most common conversion is from any type into a string variable, so all types have a method named ToString that they inherit from the System.Object class.

The ToString method converts the current value of any variable into a textual representation. Some types can't be sensibly represented as text so they return their namespace and type name.

Add the following statements to the bottom of the Main method:

int number = 12;
WriteLine(number.ToString());
bool boolean = true;
WriteLine(boolean.ToString());
DateTime now = DateTime.Now;
WriteLine(now.ToString());
object me = new object();
WriteLine(me.ToString());

Run the console application and view the output:

12
True
27/01/2017 13:48:54
System.Object

Converting from a binary object to a string

When you have a binary object that you want to store or transmit, it is best not to send the raw bits, because you never know how those bits could be misinterpreted, for example, by the network protocol transmitting them or another operating system that is reading the store binary object.

The safest thing to do is to convert the binary object into a string of safe characters. Programmers call this Base64 encoding.

The Convert type has a pair of methods, ToBase64String and FromBase64String, that perform this conversion for you.

Add the following statements to the end of the Main method:

// allocate array of 128 bytes
byte[] binaryObject = new byte[128];

// populate array with random bytes
(new Random()).NextBytes(binaryObject);

WriteLine("Binary Object as bytes:");
for(int index = 0; index < binaryObject.Length; index++)
{
   Write($"{binaryObject[index]:X} ");
}
WriteLine();

// convert to Base64 string
string encoded = Convert.ToBase64String(binaryObject);

WriteLine($"Binary Object as Base64: {encoded}");

Note

By default, an int value would output assuming decimal notation, that is, base10. You can use format codes such as index:X to format the value using hexadecimal notation.

Run the console application and view the output:

Binary Object as bytes:
B3 4D 55 DE 2D E BB CF BE 4D E6 53 C3 C2 9B 67 3 45 F9 E5 20 61 7E 4F 7A 81 EC 49 F0 49 1D 8E D4 F7 DB 54 AF A0 81 5 B8 BE CE F8 36 90 7A D4 36 42 4 75 81 1B AB 51 CE 5 63 AC 22 72 DE 74 2F 57 7F CB E7 47 B7 62 C3 F4 2D 61 93 85 18 EA 6 17 12 AE 44 A8 D B8 4C 89 85 A9 3C D5 E2 46 E0 59 C9 DF 10 AF ED EF 8AA1 B1 8D EE 4A BE 48 EC 79 A5 A 5F 2F 30 87 4A C7 7F 5D C1 D 26 EE
Binary Object as Base64: s01V3i0Ou8++TeZTw8KbZwNF+eUgYX5PeoHsSfBJHY7U99tUr6CBBbi+zvg2kHrUNkIEdYEbq1HOBWOsInLedC9Xf8vnR7diw/QtYZOFGOoGFxKuRKgNuEyJhak81eJG4FnJ3xCv7e+KobGN7kq+SOx5pQpfLzCHSsd/XcENJu4=

Parsing from strings to numbers or dates and times

The second most common conversion is from strings to numbers or date and time values. The opposite of ToString is Parse. Only a few types have a Parse method, including all the number types and DateTime.

Add the following statements to the Main method:

int age = int.Parse("27");
DateTime birthday = DateTime.Parse("4 July 1980");
WriteLine($"I was born {age} years ago.");
WriteLine($"My birthday is {birthday}.");
WriteLine($"My birthday is {birthday:D}.");

Run the console application and view the output:

I was born 27 years ago.
My birthday is 04/07/1980 00:00:00.
My birthday is 04 July 1980.

Note

By default, a date and time value outputs with the short date and time format. You can use format codes such as D to output only the date part using long date format. There are many other format codes for common scenarios.

One problem with the Parse method is that it gives errors if the string cannot be converted.

Add the following statements to the bottom of the Main method:

int count = int.Parse("abc"); 

Run the console application and view the output:

Unhandled Exception: System.FormatException: Input string was not in
a correct format.

To avoid errors, you can use the TryParse method instead. TryParse attempts to convert the input string and returns true if it can convert it and false if it cannot. The out keyword is required to allow the TryParse method to set the count variable when the conversion works.

Replace the int count declaration with the following statements:

Write("How many eggs are there? ");
int count;
string input = Console.ReadLine();
if (int.TryParse(input, out count))
{
   WriteLine($"There are {count} eggs.");
}
else
{
   WriteLine("I could not parse the input.");
}

Run the application twice. The first time, enter 12. You will see the following output:

How many eggs are there? 12
There are 12 eggs.

The second time, enter twelve. You will see the following output:

How many eggs are there? twelve
I could not parse the count.

Note

You can also use the Convert type; however, like the Parse method, it gives an error if it cannot convert.