Ranges and indices in C# 8.0

Hello friends,

Today we will go through another C# 8 feature called Ranges and indices

Range and Indices are the great additions in the C# world. Due to these constructs, handling indexes have become fairly easy. Below is a summary of the changes in this feature.

  • System.Index represents an index in an array or sequence.
  • The ^ operator specifies the relative index from the end of an array.
  • System.Range represents a subrange of an array.
  • The Range operator .. specifies the start (Inclusive) and end (exclusive) of a range.

Following table states the comparison of old and new syntax.

Task Old-style (Before C# 8) New style (in C# 8)
Getting the second element from an array array[2] array[2] or Index idx = 2; array[idx]
Getting third the last element of an array array[array.Length-3] array[^3]
Get the sub-range of an array from element index 2 till 4 array.ToList().GetRange(2, 3); array[2..5] or Range range= 2..5; array[range]
Get the sub-range of an array from second last element till last element array.ToList(). GetRange(weeks.Length- 2, 2); array[^2..^0]; or array[^2..]; or Range range= ^2..^0; array[range]
Get the first three elements of an array. array.ToList().GetRange(0, 3); array[..3];

Now Let’s take an example to understand this more and start with traditional style as follows.

static string[] weeks = new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };

public static void ExecuteOldRangeIndicesHandling()
{
Console.WriteLine($"Third element of an array is: {weeks[2]}"); Console.WriteLine($"Second last element of an array is:
{weeks[weeks.Length-2]}");

var midWeeks = weeks.ToList().GetRange(2, 3); 
Console.WriteLine("Elements of midWeeks array are:"); 
foreach (var week in midWeeks)
{
Console.WriteLine(week);
}

var endofWeeks = weeks.ToList().GetRange(5, 2); 
Console.WriteLine("Elements of endofWeeks array are:"); 
foreach (var week in endofWeeks)
{
Console.WriteLine(week);
}
}

Let’s have a look at the modern of C# 8 code syntax to achieve the same as follows.

public static void ExecuteNewRangeIndicesHandling()
{
Index idx = 2;
Console.WriteLine($"\nThird element of an array is:
{weeks[idx]}");
Console.WriteLine($"Second last element of an array is:
{weeks[^2]}");

Range range= 2..5; //Start from 2nd index and goes before 5th index means index 2, 3 and 4
var midWeeks = weeks[range]; 
Console.WriteLine("Elements of midWeeks array are:"); 
foreach (var week in midWeeks)
{
Console.WriteLine(week);
}

Console.WriteLine("Elements of endofWeeks array are:"); 
var endofWeeks = weeks[^2..^0];
foreach (var week in endofWeeks)
{
Console.WriteLine(week);
}
}

Finally, let’s go through the improvement in terms of unbounded arrays. With C# 8, you can just give one end to get subrange in an array as follows.

public static void ExecuteUnboundedRange()
{
var midWeeks = weeks[..3]; // Start from 0th and goes before 3rd index means index 0, 1 and 2
Console.WriteLine("First three elements of midWeeks array are:");
 
foreach (var week in midWeeks)
{
 Console.WriteLine(week);
}

Console.WriteLine("last two elements of endofWeeks array are:");

var endofWeeks = weeks[^2..]; 
foreach (var week in endofWeeks)
{
 Console.WriteLine(week);
}
}

Conclusion:

You can see that C# 8 syntax is crisper and easy to use. The new syntax is clearer to know what subrange is being used. This can be pretty useful when performing analysis on the subrange of an array or sequence. One of the scenarios of using the Indices and Ranges is calculating moving average in a loop.

Default Interface Implementation in C# 8.0

Hello friends, Hope you are doing well.

I am starting a series to go through the new C# 8 features one by one to cover all the new features and enhancements.

C# 8 is getting released soon along with .NET Core 3.0 and it has several new features and enhancements to give more power to developers and make their day to day coding even easier. At the time of writing this article, C# 8 is available as preview version 5.

In this article, we will go through the Default implementations of interface members however first let’s have a look at the setup required to work on C# 8.

Prerequisite to use the C# 8 (Preview Version):

  • Visual Studio 2019
  • Change the Language Version Setting
    • Right click on your project, click Properties, click Build, click Advanced and then select “C# 8.0 (beta)” in the Language Version.
  • Enable the .NET Core SDK
    • In the menu bar, click Tools, click Options, expands Environment, click Preview Features and then check the box that says “Use Previews of the .NET Core SDK” as follows.

Background and Use-Case:

Default Interface Implementation is based on Traits programming technique where reuse of methods between unrelated classes is promoted. This is also available on other programming languages, for example, its present is java in the name of Default Methods.

Now let’s try to understand the use cases that this feature is trying to solve.

Consider a case when you want to add some interface method and you fear that such action can lead to implementing that method in all the concrete classes.

The default implementation of the interface is here to address the situation

mentioned above as it allows us to add interface members with implementation or code block. This feature is designed in such a way that concrete classes have no clue on the existence of default interface and hence there is no need to implement such members.

Default interface implementation is useful especially in cases when you want to add additional contracts gracefully to accommodate new requirements without disturbing the existing contracts.

Point to note here is if you don’t provide implementation to interface members, it will be treated as regular interface member and classes implementing interface needs to provide implementations to all its members.

Implementation:

Let’s take an example to understand about how to work with it.

interface ILogger
    {
        void Log(LogLevel level, string message);
        void Log(Exception ex) => Log(LogLevel.Error, ex.ToString());
    }

class CustomLogger : ILogger
    {
        public void Log(LogLevel level, string message)
        {
            Console.WriteLine($"{level.ToString()}: Hello readers of C# 8!! {message}");
        }       
    }

class DefaultInterfaceImpl
    {
        public static void DoDivide()
        {
            try
            {
                int a = 3;
                int b = 0;
                int c = a / b;
            }
            catch (DivideByZeroException ex)
            {
                ILogger logger = new CustomLogger(); // Converting to interface               
                logger.Log(ex); // Calling new Log overload
                       }
               }
      }

As you can see in the code above that default interface member is invoked from catch block despite concrete class CustomLogger has not implemented it. Before calling Log method, the class instance is converted to ILogger interface

Now let’s try to call the Log method only with CustomLogger class as following.

CustomLogger logger2 = new CustomLogger(); 
logger2.Log(ex);

The above code will result in a compile-time error that “There is no argument given that corresponds to the required formal parameter ‘message’ of ‘CustomLogger.Log(LogLevel, string)” simpy because CustomLogger class has no information about default interface members.

Now let’s go a little deeper on the internals of default interface implementation and have a look at the modified interface as following.

interface ILogger2
    {
        abstract void Log(LogLevel level, string message);
        virtual void Log(Exception ex) => Log(LogLevel.Error, ex.ToString());
    }

The above code will give compile-time errors in previous versions of C# 8 as following.

And this code will perfectly compile in C# 8 and the reason is in C# 8 regular interface members are classified as abstract and default interface members are designed as virtual and hence declaring them as abstract or virtual doesn’t throw any error.

Making default interface members as virtual gives the flexibility to extend their implementations based on the requirement.

To have a look at the same in action let’s add a new class by implementing the ILogger2 interface as follows.

class CustomLogger2 : ILogger2
        {
            public void Log(LogLevel level, string message)
            {
                Console.WriteLine($"{level.ToString()}: Hello readers of C# 8!! {message}");
            }
            public void Log(Exception message)
            {
                Console.WriteLine($"From: Overridden method: Hello readers of C# 8!! {message}");
            }
        }

And add a new method in an existing class DefaultInterfaceImpl as following.

public static void DoDivide2()
            {
                try
                {
                    int a = 3;
                    int b = 0;
                    int c = a / b;
                }
                catch (DivideByZeroException ex)
                {
                    ILogger2 logger = new CustomLogger2(); // Converting to interface               
                    logger.Log(ex); // Calling new Log overload
                }
            }

Upon running the above code, the overridden version of Log method

(Inside CustomLogger2) will be called and the message “From: Overridden method: Hello readers of Learning C# 8!!” will be printed.

You might have noticed that override keyword has not been added in Log method

in CustomLogger2 class and this differentiates with the regular virtual and override.

Summary:

Default interface implementation is the most awaited feature that has been added as part of C# 8. It helps to add default implementation without disturbing existing contracts.

Hope you have liked the article. Please share your comments or suggestions.

String and StringBuilder in Design and Action

Hello Friends, Lets have a look at old yet relevant point about String and String Builder. Both constructs are meant to manipulate the strings but in their own way.

A string (namespace: System.String ) is a sequential collection of Unicode characters that represent text. A String object is immutable (read-only) and a sequential collection of System.Char objects that represent a string. The maximum size of a String object in memory is 2 GB (about 1 billion characters).

Although a string is a reference type, the equality operators (== and !=) are defined to compare the values of string objects, not references. However after boxing, the comparison happens on string instances.

  • string a = “prakash”;  
  • string b = “p”;  
  • b += “rakash”;  // Append to contents of ‘b’  
  • Console.WriteLine(a ==b);  //True  
  • Console.WriteLine((object)a == (object)b); //False as after boxing the //comparison happens on string instance and both are differentstring instance   

On the other hand, a StringBuilder (System.Text.StringBuilder) represents a mutable string of characters. This class cannot be inherited.

The default capacity of this object is 16 characters, and its maximum capacity is more than 2 billion characters.

  • StringBuilder sb = new StringBuilder();  
  • sb.Append(“Prakash”);  
  • sb.Append(“Tripathi”);  
  • Console.WriteLine(sb.ToString());  

How it works

The compiler creates a new string object for each string concatenation to hold the new sequence of characters (in other words the existing string and the new data), and that new object is assigned back to the variable.

For example, if you are concatenating two strings then string concatenation would allocate a new string of length (str1.Length + str2.Length) and copy the first and second strings into place.

This results in many re-copying of strings, for example concatenation of N strings (var result = str1 + str2 + str3 + … + strN;) will require N-1 allocations and N-1 copy operations. This can get very expensive for a large N.

On the other hand, a StringBuilder object maintains a buffer to accommodate the concatenation of new data. New data is appended to the buffer if room is available; otherwise, a new, larger buffer is allocated, data from the original buffer is copied to the new buffer, and the new data is appended to the new buffer.

For example, if you are concatenating 20 strings then StringBuilder simply appends one after another to its buffer and finally returns the result when requested.When to use what

Do not use a string when you don’t know the number of concatenations or if the number of concatenations are karge. For example it’s not good to use a string in the following example.

  • //Inefficient code! Do not use!  
  • string x = “”;  
  • for (int i = 0; i < 100000; i++)  
  • {  
  •     x += “!”;  
  • }  

Use the String class if you are concatenating a fixed number of String objects. In that case, the compiler may even combine individual concatenation operations into a single operation especially in the case of the concatenation of string literals as below.

  • //Efficient code  
  • stringx = “Hello” + ” ” + “Prakash”;  

On the other hand, do not use StringBuilder for trivial concatenation since allocation and initialization of StringBuilder takes some time that may not be useful for a fewer number of concatenations.

For example, the following is the bad example of StringBuilder.

  • //Inefficient code! Do not use!  
  • StringBuilder sb = new StringBuilder();  
  • sb.Append(“Hello”);  
  • sb.Append(“Prakash”);  
  • Console.WriteLine(sb.ToString());  

Use a StringBuilder object if you are concatenating an arbitrary number of strings; for example, if you’re using a loop to concatenate a random number of strings of user input. Or in the case of a big loop as in the following.

  • //Efficient code  
  • StringBuilderbuilder = new StringBuilder();  
  • for (int i = 0; i < 100000; i++)  
  • {  
  •     builder.Append(“!”);  
  • }  
  • x = builder.ToString();  

Performance Comparison

I have tested the performance comparison of String and StringBuilder in a large loop (100000 iterations) in my machine and the following are the findings.

  • int dt1 = DateTime.Now.Millisecond;  
  • Console.WriteLine();  
  • string x = “”;  
  • for (int i = 0; i < 100000; i++)  
  • {  
  •     x += “!”;  
  • }  
  • int dt2 = DateTime.Now.Millisecond;  
  • Console.WriteLine(“Time taken in string concatenation: {0} MS”, dt2 – dt1);  
  • int dt3 = DateTime.Now.Millisecond;  
  • StringBuilderbuilder = new StringBuilder();  
  • for (int i = 0; i < 100000; i++)  
  • {  
  •    builder.Append(“!”);  
  • }  
  • x = builder.ToString();  
  • int dt4 = DateTime.Now.Millisecond;  
  • Console.WriteLine(“Time taken in StringBuilder concatenation: {0}MS”, dt4 – dt3);  

Output 

StringBuilder in C#

Please note that the difference in time in both can be even more when doubling the iterations. Also the results above could vary based on the machine configuration.

Some Tips 

StringBuilder can be even more efficient when limiting the size to its constructor to avoid doubling its buffer size when it runs out of room.

Also if you have an array of things to concatenate, consider calling “String.Concat” explicitly or “String.Join” if you need a delimiter.

References: MSDN and Stackoverflow