C# Tips and Tricks You might not have heard of

A series of helpful but not that known C# features you could find useful.

1. Playing a sound to a console application

Let's start with a simple one. How many times have you written a console application that prints something to the output window? If you wonder how you would also play a short beep in that case, you could simply add \a in the message.

\a Triggers a system alert (beep). For console programs, this can be an audio clue to the user.

static void Main(string[] args)
{
Console.WriteLine("Beep... \a");
}

2. The checked Keyword

Assume you have a new method within Program that attempts to add two bytes, each of which has been assigned a value that is safely below the maximum (255). If you were to add the values of these types (casting the returned int to a byte), you would assume that the result would be the exact sum of each member.

static void ProcessBytes()
{
byte b1 = 100;
byte b2 = 250;
byte sum = (byte)Add(b1, b2);
// sum should hold the value 350. However, we find the value 94!
Console.WriteLine("sum = {0}", sum);
}

When you wrap a statement (or a block of statements) within the scope of the checked keyword, the C# compiler emits additional CIL instructions that test for overflow conditions that may result when adding, multiplying, subtracting, or dividing two numerical data types.

static void ProcessBytes()
{
byte b1 = 100;
byte b2 = 250;
// This time, tell the compiler to add CIL code
// to throw an exception if overflow/underflow
// takes place.
try
{
byte sum = checked((byte)Add(b1, b2));
Console.WriteLine("sum = {0}", sum);
}
catch (OverflowException ex)
{
Console.WriteLine(ex.Message);

Notice that the return value of Add() has been wrapped within the scope of the checked keyword.
Because the sum is greater than a byte, this triggers a runtime exception. Notice the error message printed
out via the Message property.

You can also define a "checked scope":

checked
{
byte sum = (byte)Add(b1, b2);
Console.WriteLine("sum = {0}", sum);
}

3. Returning the status of a console application on close

While a vast majority of your Main() methods will return void as the return value, the ability to return an int from Main() keeps C# consistent with other C-based languages. By convention, returning the value 0 indicates the program has terminated successfully, while another value (such as -1) represents an error condition (be aware that the value 0 is automatically returned, even if you construct a Main() method prototyped to return void).

static int Main(string[] args)
{
Console.WriteLine("Return error code -1 ...");
//...
return -1;
}

Now you are able to capture the return value of Main() with the help of a batch file for example.

4. The "params" Parameter Modifier

This parameter modifier allows you to send in a variable number of arguments as a single logical parameter. A method can have only a single params modifier, and it must be the final parameter of the method. In reality, you might not need to use the params modifier all too often; however, be aware that numerous methods within the base class libraries do make use of this C# language feature.

static double CalculateAverage(params double[] values)
{
Console.WriteLine("You sent me {0} doubles.", values.Length);
double sum = 0;
if(values.Length == 0)
return sum;
for (int i = 0; i < values.Length; i++)
sum += values[i];
return (sum / values.Length);
}

5. Discard not needed out parameters

if you don’t care about the value of an out parameter, you can use a discard as a placeholder.
For example, if you want to determine whether a string is a valid date format but don’t care about the parsed
date, you could write the following code:

if (DateTime.TryParse(dateString, out _)
{
//do something
}

6. Local Functions

Another new feature introduced in C# 7 is the ability to create methods within methods, referred to officially as local functions. A local function is a function declared inside another function.

static int AddWrapper(int x, int y)
{
//Do some validation here
return Add();
int Add()
{
return x + y;
}
}

7. Custom type conversions

Have you ever wondered how you can simply assign an int value to a "Square" object, where the int value is the side of the square? Look at the following example:

public static implicitoperator Square(Rectangle r)
Square sq2 = 90;
Console.WriteLine("sq2 = {0}", sq2);

public static implicit operator Square(int sideLength)
{
Square newSq = new Square {Length = sideLength};
return newSq;
}

Typically, this technique will be most helpful when you’re creating .NET structure types, given that they are unable to participate in classical inheritance (where casting comes for free).

8. Extension methods for interfaces

.NET 3.5 introduced the concept of extension methods, which allow you to add new methods or properties to a class or structure, without modifying the original type in any direct manner. Most of the time, you define extension methods to extend classes with new functionality via extension methods.

It is also possible to define an extension method that can only extend a class or structure that implements the correct interface.

public static void PrintDataAndBeep(this System.Collections.IEnumerable iterator)

9. Understanding Lazy Object Instantiation

When you are creating classes, you might occasionally need to account for a particular member variable in code, which might never actually be needed, in that the object user might not call the method (or property) that makes use of it. Especially if the member variable in question requires a large amount of memory to be instantiated.

While you could manually add some code to ensure the large object is created only if used (perhaps using the factory method design pattern), there is an easier way.

The base class libraries provide a useful generic class named Lazy<>, defined in the System namespace of mscorlib.dll. This class allows you to define data that will not be created unless your codebase actually uses it.

// Default constructor of MyClass is called when the Lazy<>
// variable is used.
private Lazy<MyClass> myClass = new Lazy<MyClass>();

Lazy object instantiation is useful not only to decrease allocation of unnecessary objects. You can also use this technique if a given member has expensive creation code, such as invoking a remote method or communication with a relational database.

10. C# 7 _ as Digit Separator in Numeric Literals

Here is a bit of C# 7 code in Visual Studio 2017 that says it all. Notice the _ character when declaring numbers using decimal, hexadecimal, or binary notation. Although a subtle change the numbers are definitely easier to read.

// C# 7 Numeric Literal Improvements
// Use _ as separator inside numeric literals in C# 7.

// decimal notation
var balance = 2_435_951.68;
balance += 227_652;
Debug.WriteLine($"Balance = {balance}.");

// hexadecimal notation
var num = 0x01_00;
num += 1;
Debug.WriteLine($"num = {num}.");

// binary notation
var num2 = 0b1_0000_0000;
num2 += 1;
Debug.WriteLine($"num2 = {num2}.");


.NET
c sharp tricks

02.03.2019

Acasa
pinte dan
About the Author

Dani Pinte

Dani is a software developer with over 10 years experience in developing Desktop and Web applications using Microsoft .NET Technologies. He is passionate about software, sports, travel and motorsport.



docker containers

Managing Containers in Docker