C#/ASP.NET Core Advanced features
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;
- e.g)
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
- e.g.)
-
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 ofstring
to concatnate strings. - Use
Dictionary
instead ofList
when you need fast search in loop- Time complexity(Dictionary) -> O(1)
- Time complexity(List) -> O(n)
- Use
Parallel.For
orParallel.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
- Securing responsiveness on UI thread from time-consuming tasks
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\">"
-><"123">
- Use
System.Text.Encodings.Web.HtmlEncoder
- e.g)
- Encode HTML string
- 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
thank you