DEV Community

Cover image for C# Tips and Tricks - Part 3
Mohamad Lawand
Mohamad Lawand

Posted on

C# Tips and Tricks - Part 3

This is part 3 of the C# tips and tricks you can find the links below

part 1: https://dev.to/moe23/c-tips-and-tricks-part-1-3jj9
part 2: https://dev.to/moe23/c-tips-and-tricks-part-1-2hm1

In this article we will be covering:

  • Async Main
  • Tuples
  • Value Equality for a class

You can watch the full video on YouTube

You can get the source code on GitHub:
https://github.com/mohamadlawand087/v35-ctte3

Async Main

This will allow us to add the async word into our main entry point, we can utilise it for

When we want to run async method from the main method, previously we could not await the main method. We need to do alot of wiring to make it work

So previously we need to something like this

static void Main(string[] args)
{

    // Old implementation - utilise the Async method
    var result = DoAsyncWork().GetAwaiter().GetResult();

    Console.WriteLine(result.ToString());
}

// Have an async method
private static async Task<string> DoAsyncWork()
{
    return await new HttpClient().GetStringAsync("https://docs.microsoft.com");
}
Enter fullscreen mode Exit fullscreen mode

New implementations

static async Task Main(string[] args)
{
    // New implementation -  Direct call to async 
   var res =  await new HttpClient().GetStringAsync("https://docs.microsoft.com");
   Console.WriteLine(res.ToString());
}
Enter fullscreen mode Exit fullscreen mode

Tuples

a light wait syntex used to represent data. With tuples we remove the complexity of having classes and records. We can define simple data structure with multiple feilds using tuples.

There is 2 types

  • Name tuple
  • Unnamed tuple

Although its a light weight syntax, it has really powerfull features that we can utilise

Named tuple where we specify the name of the field, so we declare a name variable. Now that i have specified the name of the field i can use it and refer to it when i utilise the tuple

// Name Tuple
var named = (FirstName: "Mohamad", LastName: "Lawand");
Console.WriteLine($"{named.FirstName}, {named.LastName}");
Enter fullscreen mode Exit fullscreen mode

Unnamed tuple, we are not giving a name for each feild and we utilise the generic way of calling the feilds inside the tuple by using item1 item 2

// Unamed Tuple 
var unnamed = ( "one",  "two");
Console.WriteLine($"{unnamed.Item1}, {unnamed.Item2}");
Enter fullscreen mode Exit fullscreen mode

As well with tuple we can run equaliy operators

var item1 = (x: 23, y: 40);
var item2 = (x: 23, y: 40);
// utilising the equality operator
Console.WriteLine(item1 == item2);
Enter fullscreen mode Exit fullscreen mode

So basically it recognise what is this object and the data model of the object and all of the fields in it and still gives us the ability to compare.

How to define value equality for a class

When we define a class or struct, you decide whether it makes sense to create a custom definition of value equality (or equivalence) for the type. Typically, we implement value equality when we expect to add objects of the type to a collection, or when their primary purpose is to store a set of fields or properties.

Our implementation should follow the five guarantees of equivalence (for the following rules, assume that xy and z are not null):

  1. The reflexive property: x.Equals(x) returns true.
  2. The symmetric property: x.Equals(y) returns the same value as y.Equals(x).
  3. The transitive property: if (x.Equals(y) && y.Equals(z)) returns true, then x.Equals(z) returns true.
  4. Successive invocations of x.Equals(y) return the same value as long as the objects referenced by x and y aren't modified.
  5. Any non-null value isn't equal to null. However, x.Equals(y) throws an exception when x is null. That breaks rules 1 or 2, depending on the argument to Equals.
class Bus : IEquatable<Bus> {
            public string Name { get; private set; } // init
            public string Color { get; private set; } // init

            public Bus(string name, string color)
            {
                if(string.IsNullOrEmpty(name))
                {
                    throw new ArgumentException("Bus must have a name");
                }

                Name = name;
                Color = color;
            }

            public override bool Equals(object obj) => this.Equals(obj as Bus);

            public bool Equals(Bus b)
            {
                if(b is null)
                {
                    return false;  
                }

                if(Object.ReferenceEquals(this, b))
                {
                    return true;
                }

                if(this.GetType() != b.GetType())
                {
                    return false;
                }

                return (Name == b.Name) && (Color == b.Color);
            }

            public static bool operator == (Bus b1, Bus b2)
            {
                if(b1 is null)
                {
                    if(b2 is null)
                    {
                        return true;
                    }

                    return false;
                }

                return b1.Equals(b2);
            }

            public static bool operator !=(Bus b1, Bus b2) => !(b1 == b2);
        }
Enter fullscreen mode Exit fullscreen mode
var busA = new Bus("School Bus", "Yellow");
var busB = new Bus("School Bus", "Yellow");
var busC = new Bus("Public Transport", "Red");
Bus busD = null;

Console.WriteLine($"busA.Equals(busB): {busA.Equals(busB)}");
Console.WriteLine($"busA == busB : {busA == busB}");
Console.WriteLine($"busA.Equals(busD) {busA.Equals(busD)}");
Console.WriteLine($"busA.Equals(busC): {busA.Equals(busC)}");
Console.WriteLine($"busA == busC : {busA == busC}");
Enter fullscreen mode Exit fullscreen mode

Top comments (0)