C# Tips and Tricks - Part 3
Tags: dotnet, csharp
Published: 2021-06-19 18:55:38

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
{% youtube tC_ilWPAh9Q %}

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

```csharp
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");
}
```

New implementations

```csharp
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());
}
```

### 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

```csharp
// Name Tuple
var named = (FirstName: "Mohamad", LastName: "Lawand");
Console.WriteLine($"{named.FirstName}, {named.LastName}");
```

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

```csharp
// Unamed Tuple 
var unnamed = ( "one",  "two");
Console.WriteLine($"{unnamed.Item1}, {unnamed.Item2}");
```

As well with tuple we can run equaliy operators

```csharp
var item1 = (x: 23, y: 40);
var item2 = (x: 23, y: 40);
// utilising the equality operator
Console.WriteLine(item1 == item2);
```

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 `x`, `y` 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`.

```csharp
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);
        }
```

```csharp
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}");
```