Design Patterns for c Student (1)
Design Patterns for c Student (1)
They are a valuable tool for writing software that follows best practices. They are used by virtually every
programming language—in APIs, frameworks, and libraries—to promote consistency, maintainability,
and clarity.
1. Leverage proven solutions, reducing the chance of introducing flawed or inefficient approaches.
2. Accelerate development, since you don’t have to reinvent effective solutions and can use a shared
vocabulary (for example, naming a solution “Factory Method” makes its intent immediately clear).
Not all problems require a pattern, and misuse can lead to over-engineering, however it’s valuable to
learn.
Injection
- the term “Injection” in DI is the term used when you passed an object (usually a class) to a method
when you invoke it.
Dependency Injection (DI) is just one way to implement the broader principle of Inversion of Control
(IoC).
There are various ways to achieve Inversion of Control, including the Service Locator pattern and most commonly,
Dependency Injection (DI).
Today, IoC is considered a best practice, and nearly all major modern frameworks use it to some degree.
References:
• https://learn.microsoft.com/en-us/training/modules/configure-dependency-injection/2-understand-dependency-
injection
• https://learn.microsoft.com/en-us/training/modules/configure-dependency-injection/?source=recommendations
• https://www.baeldung.com/cs/ioc
NOTE: I used a struct in the following example. The snippet contains the explanation of
what a struct is
//Usage
// injecting the ConsoleLogger
OrderService theOrderService = new OrderService(new ConsoleLogger());
theOrderService.PlaceOrder(new Order());
// Struct - just like a class but a class is a reference type a struct is a value type
// a value type is when you assign it to a variable, its value gets passed whereas
// a reference type is when you assign it to a variable; a reference to it gets passed;
// to illustrate:
// Order var1 = new Order("abc123", "Order 1");// Order is a struct; a value type
// Order var2 = var1; // var2 gets a copy of the object var1;
// changes you make to var2 will not affect var1 and vice versa
// var1.Name = "changed order";
// Console.WriteLine(var1.Name);
// now var2.Name is still "Order 1";
// Console.WriteLine(var2.Name);
// Reference type:
// ConsoleLogger var3 = new ConsoleLogger();// ConsoleLogger is a class; a reference type
// ConsoleLogger var4 = var3; // var2 gets a copy of the object var1;
// var3.ReferenceDemoField = "demo Field updated";
// Console.WriteLine(var3.ReferenceDemoField);
// changes you make to var3 will affect var4 and vice versa
// Console.WriteLine(var4.ReferenceDemoField);
public struct Order {
public string Id { get; }
// intentionally making this public
// so it can be modified from the outside for demo directly
public string Name { get; set; }
public Order()
{
Id = "order-0";
Name = "Order 0";
}
// Abstraction
public interface ILogger
{
void Log(string message);
}
// Concrete implementation
public class ConsoleLogger : ILogger
{
public string ReferenceDemoField= "What is my value?";
public void Log(string message) => Console.WriteLine(message);
}
When you say a class is a singleton it means that class will only have one instance when the program
is running.
In .NET Core’s DI system, the services like ILogger<T> are registered as a singleton.
The Singleton is implemented by making class constructors as private and using lazy initialization.
The singleton uses lazy initialization so that the object will only be created when GetInstance is called for
the first time.
// Private constructor
private Logger() { }
// Static method to return the instance
public static Logger GetInstance()
{
// this is what we call Lazy initialization
if (instance == null)
{
instance = new Logger();
}
return instance;
}
https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Logging/src/LoggingSer
viceCollectionExtensions.cs
Illustration:
Using the ILogger
1. Create an MVC app.
Visual Studio:
VS Code:
return View();
}
dotnet run
Visit the url used to launch the web app. This can be seen in the terminal.
OR
Run ->Start Debugging
This will launch a browser that loads the url for the app.
In the example, the PersonService will only be used in the Index method, so there is no need to put it in
the constructor like in the previous example. It can also be used in other parts of the code like in other
controllers.
1. Add the following files in your mvc app inside a new directory called “MyServices”.
IPerson.cs
namespace MyServices;
return View();
}
4. Edit Views/Home/Index.cshtml
@{
ViewData["Title"] = "Home Page";
bool isSingleton = ViewData["isSingleton"] as bool? == true;
<div class="text-center">
<h1 class="display-4">Welcome @ViewData["name"]</h1>
<p>Person service @( isSingleton ? " IS a singleton." : " IS NOT a singleton.")</p>
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with
ASP.NET Core</a>.</p>
</div>
Other sample at
https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/mvc/controllers/dependency-
injection/sample
Reference:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-
9.0
https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection
ILogger is a generic type and we supplied the Controller Class as the type.
As we have tackled in the previous discussions, you use Generics to enable a class to accommodate
varying types of data in a type-safe way.
The ILogger needs the Type inside its implementations so it can determine which class triggered the log.
In the default implementation of ILogger<T>, the category name is typically set to the full name of the
type T (i.e., typeof(T).FullName). This process involves examining the type metadata at runtime to retrieve
its full name, which is a form of reflection.
Code Reflection
Reflection is a feature in a programming language that allows a running program to examine and interact
with its own structure and metadata at runtime. For example, a class can use Reflection to obtain the
names of its members, inspect their types, or even discover its own name.
See
https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Logging.Abstractions/sr
c/LoggerT.cs#L50
Metadata is the structured “index” or “table of contents” that the C# compiler embeds in your assembly
(DLL, EXE, or package). It describes every component—types, methods, properties, fields, attributes,
references, and so on.
Reflection is the runtime API that reads that metadata, enabling frameworks and your own code to
discover and interact with those components.
What you see in IntelliSense (member lists, parameter info, Quick Info tooltips, etc.) is drawn directly
from the same metadata that reflection reads at runtime. At design time, Visual Studio (via the Roslyn
compiler APIs) loads each referenced assembly’s metadata tables—types, method signatures, property
definitions, XML doc comments, and attributes—and presents them in the editor without actually
executing your code.
The most used reflection API for programmers is for getting tthe type.
Once you get the Type of an object (via typeof(T) or myObj.GetType()), you can inspect or manipulate its
metadata — for example:
using System.Reflection;
C# lets you define your own attributes to attach custom metadata to types (classes, interfaces etc.),
members, parameters etc.
Attributes in c# allow programmers to attach declarative information or metadata to code. You can
define custom attributes by defining classes that directly derives from the System.Attribute
class(https://learn.microsoft.com/en-us/dotnet/api/system.attribute?view=net-9.0). The custom
attribute can be applied just like any other built-in attribute.
• The product as a class that has flexible parts. It is not required that there must be a "common
required part" that is always present in all variations.
• There is one builder per product variation: each builder constructing a specific product variation.
The builder returns the object of type “product”.
• Have a base for all the builders to ensure structure (all required methods are present in all types)
that will serve as the type for all builders (used in DIP).
• Optionally there can a director that determines which builder to use based on a parameter.
.NET builder example:
.NET have several APIs that uses the builder pattern. The line of code below(found in Program.cs of an
mvc app) creates a builder. The builder it creates is the WebApplicationBuilder. It is the concrete builder,
that creates a WebApplication object through its build() method.
the builder creates different variations of a WebApplication object depending on the calls made before its
.build() is called.
The other type of concrete builder is the HostApplicationBuilder. This is the builder for non web
applications.
These two builders both implement IHostApplicationBuilder interface. Their common parts are the
properties(properties in c# both functions as a data member and a function member – used as a data but
treated as a method by .NET Compiler):
• Properties
• Configuration
• Environment
• Logging
• Metrics
• Services
See code illustrations in the following:
• Builder design pattern code illustration, https://refactoring.guru/design-patterns/builder/csharp/example
• IHostApplicationBuilder source code,
https://github.com/dotnet/runtime/blob/0a33e18a0bccc10a0c4646dbf5b0fc70cbb3aa44/src/libraries/Microsoft.Ext
ensions.Hosting.Abstractions/src/IHostApplicationBuilder.cs
• HostApplicationBuilder source code,
https://github.com/dotnet/runtime/blob/0a33e18a0bccc10a0c4646dbf5b0fc70cbb3aa44/src/libraries/Microsoft.Ext
ensions.Hosting/src/HostApplicationBuilder.cs
• WebApplicationBuilder source code,
https://github.com/dotnet/aspnetcore/blob/main/src/DefaultBuilder/src/WebApplicationBuilder.cs
Instructing builder to customize logging providers for the WebApplication object that will be built.
NOTE: When trying out the example, make sure to clear the old logs first in the terminal and debug
window before you make changes and re-run the app.
Example 2:
See more complete example earlier “Creating your own singleton in c#”.
In summary, you add the customizations via the exposed properties before the call to .Build().
References:
• https://learn.microsoft.com/en-us/archive/msdn-magazine/2005/july/discovering-the-design-
patterns-you-re-already-using-in-net
• https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/common-design-patterns
• https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-
apis/webapplication?view=aspnetcore-9.0