C# 10.0 New Feature - Allow Constant interpolated strings with code example

Continuing with the example in the earlier blog, suppose we want to display different messages based on the customer type, something like this with respect to first-time customer not getting a discount since we are super-friendly!



But, before that, decided to do some clean-up and as a result created separate projects as follows:


If you are sharp enough, I borrowed a bit from the Sitecore Helix pattern in the sense that I treated DiscountLogic and MessageLogic as separate features and so, pushed those into separate projects.

This is how my solution looks after the segregation:


The Repository project has the ICustomer.cs and it looks like this:

namespace Repository
{
    public interface ICustomer
    {
        double BillTotal { get; set; }
        double DiscountAmount { get; set; }

        ICustomer CalculateDiscount(int percent);
    }
}

The Customer.cs base class looks like this now:

namespace Repository
{
    public class Customer : ICustomer
    {
        public double BillTotal { get; set; }
        public double DiscountAmount { get; set; }

        public ICustomer CalculateDiscount(int percent)
        {
            DiscountAmount= BillTotal * percent / 100;
            return this;
        }
    }
}

The FirstTime, Regular and Membership classes all inherit from Customer base class and they all reside in the Repository project.

Next, the DiscountLogic project holds the DiscountDecider class and it looks like this:

using Repository;
namespace DiscountLogic
{
    public class DiscountDecider
    {
        public ICustomer ClassifyCustomer(ICustomer customer) => customer switch
        {
            Regular => customer.CalculateDiscount(1),
            Membership => customer.CalculateDiscount(3),
            _ => customer.CalculateDiscount(0)
        };
    }
}

I added a new project called MessageLogic and it has a MessageDecider.cs and it looks like this:

using Repository;

namespace MessageLogic
{
    public class MessageDecider
    {
        const string GeneralCustomerType = "Customer Discount applicable";
        const string FirstTimeCustomerType = "Customer Discount NOT applicable";
        const string SorryDiscountMessage = "Sorry,";
        const string HappyDiscountMessage = "Congrats,";
        const string ForyouDiscountMessage = "for you";
        const string FirstTimeCustomerMessage = $"{SorryDiscountMessage} {FirstTimeCustomerType} {ForyouDiscountMessage}";
        const string OtherCustomerMessage = $"{HappyDiscountMessage} {GeneralCustomerType} {ForyouDiscountMessage}";
        const string CustomerDiscountforBillMsg = "; Customer Discount for Bill Amount";

        public string ClassifyCustomer(ICustomer customer) => customer switch
        {
            Regular => GetGeneralMessage(customer),
            Membership => GetGeneralMessage(customer),
            _ => GetFirstTimeMessage()
        };

        private string GetGeneralMessage(ICustomer customer)
        {
            return $"{OtherCustomerMessage} {CustomerDiscountforBillMsg} { customer.BillTotal} = ${ customer.DiscountAmount}";
        }

        private string GetFirstTimeMessage()
        {
            return FirstTimeCustomerMessage;
        }
    }
}

The two highlighted lines are the ones depicting Constant interpolated strings. In the sense, you can concatenate constants storing strings into another constant. This was not possible in the earlier versions.

Next, the client logic looks like this in Program.cs:

using Repository;
using DiscountLogic;
using MessageLogic;
namespace Client
{
    class Program
    {
        
        static void Main(string[] args)
        {
            MessageExample();
        }

        static void MessageExample()
        {       
            Console.WriteLine(MembershipCustomer());

            Console.ReadLine();
        }

        static string FirstTimeCustomer()
        {
            DiscountDecider discountDecider = new();
            MessageDecider messageDecider = new();

            ICustomer firstTime = new FirstTime
            {
                BillTotal = 1000
            };
            _ = discountDecider.ClassifyCustomer(firstTime);
            return  messageDecider.ClassifyCustomer(firstTime);
        }

        static string RegularCustomer()
        {
            DiscountDecider discountDecider = new();
            MessageDecider messageDecider = new();

            ICustomer regular = new Regular
            {
                BillTotal = 1000
            };
            _ = discountDecider.ClassifyCustomer(regular);
            return messageDecider.ClassifyCustomer(regular);
        }


        static string MembershipCustomer()
        {
            DiscountDecider discountDecider = new();
            MessageDecider messageDecider = new();

            ICustomer member = new Membership
            {
                BillTotal = 400
            };
            _ = discountDecider.ClassifyCustomer(member);
            return messageDecider.ClassifyCustomer(member);
        }

        static void PatternMatchingExample()
        {
            DiscountDecider discountDecider = new();
            ICustomer regular = new Regular
            {
                BillTotal = 700
            };
            var discountedCustomer = discountDecider.ClassifyCustomer(regular);
            Console.WriteLine($"Regular Customer Discount for Bill Amount {discountedCustomer.BillTotal} = ${discountedCustomer.DiscountAmount}");

            ICustomer firstTime = new FirstTime
            {
                BillTotal = 1000
            };
            discountedCustomer = discountDecider.ClassifyCustomer(firstTime);
            Console.WriteLine($"First-time Customer Discount for Bill Amount {discountedCustomer.BillTotal} = ${discountedCustomer.DiscountAmount}");

            ICustomer memberShipCustomer = new Membership
            {
                BillTotal = 400
            };
            discountedCustomer = discountDecider.ClassifyCustomer(memberShipCustomer);
            Console.WriteLine($"Membership Customer Discount for Bill Amount {discountedCustomer.BillTotal} = ${discountedCustomer.DiscountAmount}");

            Console.ReadLine();
        }
    }
}

I just segregated the invocation logic for each customer type in its own method. I can now invoke/test those separately. I also have retained the code example from the earlier blog but in a separate method named PatternMatchingExample.

An interesting line of code that uses latest C# features is this one for me -

_ = discountDecider.ClassifyCustomer(regular);

Since there is nothing used in case of this procedure except I hydrate the Customer object, _ is used as the return type by the compiler.

My _GlobalUsings.cs stays as-is since we just use System namespace in the client project and we might need it globally within that project!

Now, once I execute the console application, this is what I get as result since I'm invoking logic specific to membership customer:


If I invoke the First-time customer logic, I get this super-friendly message!



In case if I add langVersion earlier than 10.0 to MessageLogic project, I get this compile error:

Feature 'constant interpolated strings' is not available in C# 9.0. Please use language version 10.0 or greater.



Comments