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.

Store Multiple NULL values with unique data in SQL Server

Hello friends,

Sometimes we get the data that needs to be unique but can also get NULL records. Existing ways to achieve uniqueness doesn’t allow NULLs (Primary Key) or allow max one NULL (Unique Constraint).

Let’s take an example to understand this better. Suppose, you have an Employee table with fields like Id, FirstName, MiddleName, LastName, Email etc. As per requirement, email needs to be unique (if supplied), however, users are also allowed to register without entering email and hence in the database Email is a nullable field.

So how would you achieve the Email uniqueness along with having more than one NULLs.

CREATE UNIQUE NONCLUSTERED INDEX [UX_Employee_Email] ON [dbo].Employee WHERE Email IS NOT NULL

Above code will ensure that Email doesn’t have duplicate data however it may store more than one NULLs.

I hope you may have liked the tip. You share your comments.