Monday 21 June 2021

Syntactic sugar (and other nice features) in C# part 2


This time some simple things. Possibly obvious for programmers with some experience... But maybe not so obvious for everyone? Once again we're discussing C# 7.0 used with Unity.

1. You can inline an out variable declaration

The first one this time is a very little detail, which may seem obvious - but I wasn't aware it for quite some time. I think this may be quite new (since C# 7.x indeed?)

When using a method with an out param, you don't have to declare it before.

Which means you can write this:

string maybeNumber = "10";
int result;
int.TryParse(maybeNumberout result);
DoSomethingWith(result);

as this:

string maybeNumber = "10";
int.TryParse(maybeNumberout int result);
DoSomethingWith(result);

2. Long numbers made readable (works also in Python and Java)

Do you use big numbers in your code? You know, when something like long bigNumber1 = 2156454565; shows up, and you're not sure whether it's 215 millions 645 thousand and so on, or maybe 2 billions 154 millions or... Well, it's because you don't know it can be written like this:

long bigNumber2 = 2_156_454_565;

Works for other numeric types too:

double bigNumber1 = 100_123.125_324;

This way of declaring numeric literals works also in some other languages: I'm sure about Java and Python. It doesn't seem to work with C(++).

3. A short story about null values and question marks

There is something called conditional operator in most of modern programming languages. It's consists of an expression, followed by ? sign and then some value to be returned if expression is true; then : sign and a value to be returned if expression is false. Long story short:

int number = 42;
string nrDescription = (number==42)?"Ultimate answer":"An ordinary number";

It's pretty well known syntax. But in C#, there are more similar tools.

3.1 Null coalescing operator

One of them is a null coalescing operator, which may be handy when checking if something is null. Let's consider the following code (it does not make use of null coalescing operator yet, see below):

TimeZoneInfo tInfo = TimeZoneInfo.Local// this can be null
          
TimeZoneInfo nonNullTInfo1 = tInfo != null ? tInfo : TimeZoneInfo.Utc;
Console.WriteLine(nonNullTInfo1.DisplayName);

It's simple: it shows the local time zone display name, and if it's not available (TimeZoneInfo.Local is null), it fallbacks to TimeZoneInfo.Utc. It uses the "traditional" conditional operator.
And here is our alternative, using the  null coalescing operator:

TimeZoneInfo tInfo = TimeZoneInfo.Local// this can be null

TimeZoneInfo nonNullTInfo2 = tInfo ?? TimeZoneInfo.Utc;
Console.WriteLine(nonNullTInfo2.DisplayName);

A bit shorter, a bit more elegant. Can save quite a lot of typing in more complex cases.

3.2 Null conditional-operators ?. and ?[]

Let's consider following code:

class SomeClass
    {
        public void someMethod()
        {
            Console.WriteLine("Some method is working");
        }

        public TimeZoneInfo getTimeZone()
        {
            return TimeZoneInfo.Local;
        }
    };

    class Program
    {

        static void Main(string[] args)
        {
            SomeClass someObject = null;

            someObject.someMethod();

            TimeZoneInfo res = someObject.getTimeZone();

            Console.Write("Result:"+res.StandardName);
        }
    }

Obviously, this code will throw an exception. As we never initialize someObject, the call: someObject.someMethod(); will throw NullReferenceException. Of course, we can precede it with if(someObject!=null). But we can change the Main method to look like this:

static void Main(string[] args)
{
    SomeClass someObject = null;

    someObject?.someMethod();
    TimeZoneInfo res = someObject.getTimeZone();

    Console.Write("Result:"+res.StandardName);
}
What happens then? An NullReferenceException is thrown again, but it's the .getTimeZone() call that throws it! someObject?.someMethod(); is simply not called.

So let's add another ?, this time before .getTimeZone(); call:

static void Main(string[] args)
{
    SomeClass someObject = null;

    someObject?.someMethod();
    TimeZoneInfo res = someObject?.getTimeZone();

    Console.Write("Result:"+res.StandardName);
}

.someMethod();.is not called, and then .getTimeZone() is not called. What happend with res? Well, because the initializing expression can't be called, it... stays uninitialized: it's null.
Which means we get our NullReferenceException only in the call to Console.Write("Result:"+res.StandardName);. Which, by the way, can be avoided this way:

Console.Write("Result:"+res?.StandardName);

... resulting in writing to console simply Result: (the value returned by res.StandardName is simply null, which is printed as en empty char).

To sum up, the following code (consisting all the aforementioned features):

static void Main(string[] args)
{
    SomeClass someObject = null;

    someObject?.someMethod();
    TimeZoneInfo res = someObject?.getTimeZone()
    
    Console.Write("Result:"+res?.StandardName);
}

...is equivalent to the following ("old school") code:

class Program
{
    static void Main(string[] args)
    {
        SomeClass someObject = null;

        if(someObject!=null)
            someObject.someMethod();

        TimeZoneInfo res = 
        (someObject != null)?someObject.getTimeZone():null;
        
        Console.Write("Result:"+ ((res!=null)?res.StandardName:""));
    }
}

What about ?[] operator, mentioned in the midtitle? Well, it's similiar to ?. but involved using arrays. So it allows us to write:

int[] array = null;
Console.WriteLine(array?[2]);

instead of:

int[] array = null;
Console.WriteLine(array == null?"":array[2].ToString());

No comments:

Post a Comment

Python crash course part 10: inheritance and polymorphism

In the last part we've shown how to create and use a class in Python. Today we're going to talk about inheritance: wchich means cre...