Notes about .Net

Intermediate Language

NET 6 has a single BCL and two CLRs: CoreCLR is optimized for server or desktop scenarios like websites and Windows desktop apps, and the Mono runtime is optimized for mobile and web browser apps that have limited resources.

The C# compiler (Roslyn) converts code to Intermediate Language (IL) and saves this into .DLL or .EXE file. IL code is executed by .Net Virtual Machine known as CoreCLR. CoreCRL loads from IL and compiles it to native CPU instructions by JIT (Just in Time Compiler). CoreCLR differs for each OS (Mac, Windows, Linux) so that the same code can run on multiple Operating systems.

C# 9 Features

  • Minimal-code console apps / Top-level programs
  • Target-typed new / Using target-typed new to instantiate objects
  • Enhanced pattern matching 
  • Records

C# 10 Features

  • Global namespace imports
  • Constant string literals
  • File-scoped namespaces
  • Required properties
  • Record structs
  • Null parameter checks

Implicit Global Namespace Import

.Net 6 imports global namespaces implicitly depending on project type.

Digit Separator

From C#7, _ can be used as a digital seperator.

var onemillion = 1_000_000;

Dispose


public class Animal : IDisposable
{
    public Animal()
    {
        // allocate unmanaged resource
    }
    ~Animal() // Finalizer
    {
        Dispose(false);
    }
    bool disposed = false; // have resources been released?
    public void Dispose()
    {
        Dispose(true);
        // tell garbage collector it does not need to call the finalizer
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing)
    {
        if (disposed) return;
        // deallocate the *unmanaged* resource
        // ...
        if (disposing)
        {
            // deallocate any other *managed* resources
            // ...
        }
        disposed = true;
    }
}

Native Sized Int

C# 9 introduced nint and nuint keyword alias for native-sized integers, meaning that the storage size for the integer value is platform specific. They store a 32-bit integer in a 32-bit process and sizeof() returns 4 bytes; they store a 64-bit integer in a 64-bit process and sizeof() returns 8 bytes.

WriteLine($"int.MaxValue = {int.MaxValue:N0}");
WriteLine($"nint.MaxValue = {nint.MaxValue:N0}");
WriteLine($"nuint.MaxValue = {nuint.MaxValue:N0}");

int.MaxValue = 2,147,483,647
nint.MaxValue = 9,223,372,036,854,775,807
nuint.MaxValue = 18,446,744,073,709,551,615

App Trimming

Generic Math

Good practice with collections

Let's say you need to create a method to process a collection. For maximum flexibility, you could declare the input parameter to be IEnumerable<T> and make the method generic, as shown in the following code:


void ProcessCollection<T>(IEnumerable<T> collection)
{
// process the items in the collection,
// perhaps using a foreach statement
}

I could pass an array, a list, a queue, a stack, or anything else that implements IEnumerable<T> into this method and it will process the items. However, the flexibility to pass any collection to this method comes at a performance cost. One of the performance problems with IEnumerable<T> is also one of its benefits: deferred execution, also known as lazy loading. Types that implement this interface do not have to implement deferred execution, but many do. Good Practice: To improve performance, many applications store a shared copy of commonly accessed objects in a central cache. To safely allow multiple threads to work with those objects knowing they won't change, you should make them immutable or use a concurrent collection type that you can read about at the following link: https://docs.microsoft.com/en-us/dotnet/ api/system.collections.concurrent

But the worst performance problem with IEnumerable<T> is that the iteration has to allocate an object on the heap. To avoid this memory allocation, you should define your method using a concrete type, as shown highlighted in the following code:


void ProcessCollection<T>(List<T> collection)
{
// process the items in the collection,
// perhaps using a foreach statement
}

This will use the List<T>.Enumerator GetEnumerator() method that returns a struct instead of the IEnumerator<T> GetEnumerator() method that returns a reference type. Your code will be two to three times faster and require less memory. As with all recommendations related to performance, you should confirm the benefit by running performance tests on your actual code in a product environment.

Entity Framework Core 6.0

To get the SQL Statment of EF Query, ToQueryString() can be used

You can use Global Filters to apply filtering on a table. You may have soft delete feature on a table. And you can add IsDeleted = false filter.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
...
modelBuilder.Entity()
.HasQueryFilter(p => !p.IsDeleted);
}

AsParallel()

To run LINQ query on multiple threads, you can use AsParallel extension.

var peopleStatus = people.AsParallel()
.Select(number => GetExternalStatus(number))
.ToArray();

Benchmark.Net

No comments

Powered by Blogger.