😺

C#/ASP.NET Core Advanced features

2024/04/30に公開
1

Abstruct

This article provides the insights regarding features of C# or ASP.NET Core to embody the code with clean architecture and in high performance and security.

Clean Code

Interface and Abstruct class

Both abstruct and encupslate their implementation and what the classes should provide to external components

Interface

  • Defines what members the class should have
    • method, property, indexer, event
  • Need to implement the details of the members
  • Useful for:
    • DI
    • Mock a class for unit test
    • Implementation of Strategy Pattern to reduce unnecessary conditional branchs.
interface IVehecleOperation
{
    void Run()
}

class Car : IVehecleOperation
{
    // Explicit interface member implementation:
    void Run()
    {
        // Method implementation.
    }
}

Abstruct class / method

  • A super class doesn't include its implementation
  • Cannot instanciate it.
  • Useful for:
    • provide common functionality and interface to its subclass
abstract class Vehecle
{
    public abstract void Run();
}

class Car : Vehecle
{
    public override void Run()
    {
        // Method implementation.
    }
}

Comparison

Abstruct Class Interface
Inheritance single multiple
access modifier public, protected public
method definition Can implement Cannnot implement
member variable static/instance fields static fields

Generics

Functionality for a code independent on types

  • Useful for:
    • Reuse a code in a variety of Type
    • Implementation for collection
    • Keeping type safe in compile
// T: Type
// where T: condition of the type
static T Max<T>(T a, T b)
  where T : IComparable
{
  // a and b are secured to have IComparable interface
  // Thus a and b has CompareTo() 
  return a.CompareTo(b) > 0 ? a : b;
}

Conditions pettern

condition meaning note
where T : struct Value Type
where T : class Reference Type
where T : notnull Not Null
where T : Enum Enum can use with "struct"
where T : Delegate Delegate
where T : [base class] Inherites base class
where T : [interface] Has interface
where T : new() Have constructer without arguments Should place the last
  • Can use multiple condtitions separated commas(,)
  • Cannot specify duplicated or contradicted conditions
    • e.g) where T: struct, class

Access modifiers

Encupslate class/members and protect its internal

modifiier scope usage
public anyware OK interfaces for external access
internal only in the assembly(project) as default usage
protected the class and its subclass inheritance
private only in the class encupslation
  • Default is private

in/readonly/required modifier

They are useful for prevention from invalid value in the class.

modifier target effect
in argument prohibit reassignment to an argument
readonly variable prohibit reassignment after initialisation
required parameter set the pamameter required to initialise

ref/out

Both are the function to pass argument to output result.

modifier initialization in caller assign in the callee
ref Nessesary Unnecessary
out Unnessesary Nessesary

Nullable reference type

  • NOT Nullable as default in reference time
    • But not throw error, just alert it on IDE
  • Need to add ? modifier to its type to set it Nullable
    • e.g) string? descripton;

Delegation

Functionality to pass a method to another method.

// Define delegate type
delegate void SingleDelegate(int a);
delegate void MultiDelegate(string b);

class DelegateTest
{
  static void Main()
  {
    // assign a method
    SingleDelegate single = new SingleDelegate(SayAge);
    single(18); // call the method via delegate

    // Multi deligation
    MultiDelegate multi = new MultiDelegate(SayName);
    multi += new MultiDelegate(SayHello);
    // sequential excecution SayName -> SayHello 
    multi("John"); 
  }

  static void SayAge(int age)
  {
    Console.WriteLine($"I'm {age} years old.");
  }
  static void SayName(string name)
  {
    Console.WriteLine($"My name is {name}.");
  }
  static void SayHello(string name)
  {
    Console.WriteLine($"Hello {name}.");
  }
}
  • Can delegate both static/instance methods.
  • Can delegate multiple methods
    • execute sequentially by FIFO order
  • Userful for:
    • Event handler
    • Reuse code by replacing only method

record

Something like immutable class. Generates such as Equal or ToString automatically after compile.

  • Useful for:
    • Value Object in DDD

Comparison: class/struct/record

type usage
class reference type complex and mutable structure which needs modification
struct value type simple and mutable structure which doesn't needs modification
record value type the same as class but immutable

Index and range

Convenient index tips to express more consice.

meaning note
^i i th index from the last zero start
i..j i th to j-1 th indices include i but exclude j
i.. i th index to the last
..j 0 th index to j-1 th

DI

  • Needs interface to inject
  • Configure in Program.cs
  • Inject to constructor
  • Useful for:
    • Mock injection in unit test
    • Loose coupling
    • Reusability

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<IUserSessionService, UserSessionService>();
builder.Services.AddScoped<ICartService, CartService>();
builder.Services.AddSingleton<IGlobalSettingService, GlobalSettingService>();
  • Each methods provides with diffirent lifetime of instance
method from to
AddTransient Every access of the service -> End of the instance, becoming the target of GC
AddScoped Start of HTTP request -> End of HTTP request
AddSingleton Start of application -> End of application

Constructor

public class ECommerse {
  ECommerse(IUserSessionService userSessionService,
      ICartService cartService,
      IGlobalSettingService globalSettingService) {
        ....
    }
}

Reflection

Functionality to fetch or modify metadata of class in runtime.

Type type = typeof(DateTime); // Get type name from Class
Type type = obj.GetType(); // Get type name from Object
PropertyInfo prop = type.GetProperty("MyProperty"); // Get property from type
prop.SetValue(obj, 987); // Set value to property

*Useful for:
* Debug/Unit test
* The development of generic libraries or frameworks

High Performance

Boxing and Unboxing

Conversion between Value type and Reference type.
Could cause unnecessary overhead and shouldn't use without reasons.

type memory type memory
Boxing Value type stack -> Reference type heap
Unoxing Reference type heap -> Value type stack

Boxing

int intValue = 42;
object objValue = intValue; // Boxing

Unboxing

object objValue = 42;
int intValue = (int)objValue; // Unboxing

Finalizer and Dispose pattern

Code pattern for your unique class to open in using() variables.
Implement IDisposable interface.

  • Managed resource: Managed by .NET Framework

    • e.g.) System.IO.File class
    • Should dispose when Dispose() is called
  • Unmanaged resource:

    • e.g.) Windows API to operate more detailed functionality for files
    • Should dispose when Finalizer is called
    • High cost to dispose resource here
      • Don't use it as much as possible

Implementation

// Implement IDisposable interface
class Resource : IDisposable
{
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Dispose managed resource 
        }
        // Dispose unmanaged resource
    }

    // finalizer
    ~Resource()
    {
        Dispose(false);
    }
}

Usage

using(var resource = new Resource()){
  // process resource
}

Loop

  • Use StringBuilder instead of string to concatnate strings.
  • Use Dictionary instead of List when you need fast search in loop
    • Time complexity(Dictionary) -> O(1)
    • Time complexity(List) -> O(n)
  • Use Parallel.For or Parallel.ForEach when you need to precess nested loop
    • It compute each row parallelly
Parallel.For(0, digits.Count, (i) => {
    for (int j = 0; j < digits.Count; j++) {
        var a = j;
    }
});

unsafe

A keyword to use pointer. Usually no need to use but could be the last option for performance tuning.

  • Useful for:
    • To interporate with pointer language like C
    • Fine-tuning for performance
// unsafe method
unsafe struct X
{
  byte x;
  int y;
}
unsafe void UnsafeMethod()
{
  // can use pointer
  var p = new Point();
  var pp = &p;
  int* pi = (int*)pp;
  *pi = 0x00010002;

  // keep array on stack
  int* x = stackalloc int[N];

  // fetch the size of struct
  Console.WriteLine(sizeof(X));

  // fix address not to move in GC
  var array = new[] { 1, 2, 3, 4, 5 };
  fixed (int* px = array)
  {
    for (int* p = px; p != px + array.Length; ++p)
      *p = (*p) * (*p);
  }
}
void SafeMethod()
{
  // cannot use pointer

  // unsafe block
  unsafe
  {
    // need unsafe block if you use pointer or call unsafe mehtod
    UnsafeMethod()
  }
}

.csproj

<Project Sdk="Microsoft.NET.Sdk">
 
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
    <!-- enable unsafe feature -->
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>
 
</Project>

Span<T>

Functionality to read/write sequential data without allocating memory.

  • Sequential data
    • Array
    • String
    • Binary
  • Useful for:
    • Efficient memory performance without copy or memory allocation
    • To create library which operates pointer but to call without unsafe statement.
    • Avoiding GC by the combination with stackalloc.
// creation
var span = new Span<int>(array, 2, 3);
var span = array.AsSpan().Slice(2, 3);

// Avoid unsafe block for stackalloc
// No need to use unsafe block
Span<byte> buffer = stackalloc byte[BufferSize];
// callee
// using Span<> in argument
static void Clear(Span<byte> span)
{
    unsafe
    {
        fixed (byte* pin = span)
        {
            // process
        }
    }
}

// caller
static void Main()
{
    var array = new byte[256];
    // No need to use unsafe block
    Clear(array);
}

Generics

You can generate new instance of the generic time.
But it's so slow due to using runtime type information.
Better to avoid when you have severe performance requirement.
It's possibly a better option to receive generater function.

Slow

static T[] Array<T>(int n)
    where T : new()
{
    var array = new T[n];
    for (int i = 0; i < array.Length; i++)
        array[i] = new T(); // 10 times slower than usual new()
    return array;
}

Fast

static T[] Array<T>(int n, Func<T> generator)
{
  var array = new T[n];
  for (int i = 0; i < array.Length; i++)
    array[i] = generator(); // call generator
  return array;
}

// Pass generator from caller
Array(1000, () => new MyClass());

Async programming

Background processing

a.k.a Non-blocking processing. Asyncronously process between UI and Time-consuming process.

  • Use sugarcoat syntax of async / await
    • Makes the steps serial and flat as sync processing
  • Useful for:
    • Securing responsiveness on UI thread from time-consuming tasks
      • I/O processing
      • Network processing
      • DB access
public void DoSomething(string uri) {
  var url = new Uri(uri);
  // add await to async method
  await ProcessAsync(url);
}

// add async keyword
// return type should be Task, Task<T> or void
// append Async suffix for async method
private async Task<boolean> ProcessAsync(Uri resource)
{
    var client = new WebClient();
    // add await to async method
    return await client.DownloadAsync(resource);
}

Parallel processing

Data parallelism

Execute the same processing to the diferrent dataset in multi threads

  • Suitable for the independent dataset
  • Unsuitable when the dataset needs:
    • To be reordered after all
    • To use the result of other threads

Parallel

// single thread
for (var i = 0; i < source.Length; i++)
{
    result[i] = selector(source[i]);
}

// multi threads
Parallel.For(0, source.Length, i =>
{
    result[i] = selector(source[i]);
});

LINQ

// single thread
var result = source.Select(selector);

// multi threads
var result = source.AsParallel().Select(selector);
Task parallelism

Exchange data among independent tasks simaltaniously.
The most simple way is using TPL(Task Parallel Library) to implement.

// create tasks
Task task1 = Task.Run(() =>
{
    // procces in task1
    return 1;
});
Task task2 = Task.Run(() =>
{
    // procces in task2
    return 2;
});

// wait for all task to be completed
Task.WhenAll(task1, task2);

// Aggregate the results of tasks
var res1 = task1.Result
var res2 = task2.Result;
var totalRes = res1 + res2;

Security

SQL Injection

An attack to inject malicious query through user input mechanism.

Solution

API
  • Entity Framework
    • Uses parameterised query inside
    • Could be solution for SQL Injection if you can use it.
  • Parameterised query
string query = "SELECT * FROM Users WHERE Username = @Username AND Password = @Password";
SqlCommand cmd = new SqlCommand(query, connection);
cmd.Parameters.AddWithValue("@Username", username);
cmd.Parameters.AddWithValue("@Password", password);

XSS

An attack to inject malicious script into application.
Usually Javascript is used for script via <script> tag in html.

Solution

API
  • Input Validation
    • Verify the input value and pass only genuine value
UI
  • HTMLEncode
    • Encode HTML string
      • e.g) "<\"123\">" -> &lt;&quot;123&quot;&gt;
      • Use System.Text.Encodings.Web.HtmlEncoder
  • Razor
    • Razor engine used in MVC automatically encodes all output sourced from variables

CSRF

An malicious attack to deceive the users to send malicious requests via their fraud web pages and operate the resources illigally.

Solution

API
  • Add X-CSRF-TOKEN to HTTP Header which set on its Cookie
  • Same-origin policy
    • But it disturbs interaction between your own system
    • You may adjust acceptable origins by CORS mechanism
UI
  • Avoid using GET
  • Use AntiForgeryToken

Others

.NET Framework vs .NET Core

.NET Framework .NET Core
Use case Existing Enterprize system, Desktop application for Windows Cloud Native application, Containerlized application
Platform Only Windows Cross platform (Windows、Linux、macOS)
Life Cycle Stable Frequently updated
Open Source Partially Fully

Discussion