--- marp: true paginate: true math: mathjax theme: buutti title: 15. Design Patterns in C# --- # Design Patterns in C# ## Overview * The Singleton Pattern * Reflection and attributes * Dependency Injection ## The Singleton Pattern ### The problem * In most cases, it makes no sense to create an instance of a class every time its members need to be accessed * For example, a shared resource manager that is being called from multiple classes * While a static class could be used for this, there are some problems: * As stated in [lecture 10](10-static-members-methods-and-classes#StaticClasses), static classes can only have static members * Static classes cannot be instantiated, so a reference to them cannot be passed around as a parameter * Static classes cannot inherit from other classes or implement interfaces * [And many more... ](https://www.c-sharpcorner.com/UploadFile/akkiraju/singleton-vs-static-classes/) ### The solution * The __singleton__ class is a class that benefits from all the perks of a non-static class (non-static members, inheritance, referencing…), but only one (or zero) instances of it ever exists during the lifetime of your application * For example, reading from / writing to a file that should be accessible to multiple clients, should be made into a singleton * Instead of every client directly accessing the same file (and possibly causing massive performance issues), the singleton is instantiated once and a reference to it is provided to clients * The singleton could take care of queueing the read/write requests and be the only entity accessing the actual file --- A singleton implementation could look something like this:
```csharp class Singleton { private static Singleton instance = null; private Singleton() { } public void MySingletonFunction() { Console.WriteLine ("This function is accessible anywhere!"); } public static Singleton Instance { get { if (instance == null) instance = new Singleton(); return instance; } } } ```
```csharp class Program { static void Main(string[] args) { Singleton.Instance.MySingletonFunction(); // Outputs: "This function is accessible // from everywhere!" } } ```
### Implementing a singleton pattern * The exact implementation of the singleton is out of the scope of this course, but it is important to understand that it exists and what its purpose is * Multitude of examples for different use cases are available and can be found by googling ## Reflection * Reflective programming or reflection is the ability for the program to examine or modify its own structure and behaviour * C# has special reflection methods that we can use to get information about types
* Simple reflection example to obtain type information: ```csharp int i = 42; Type type = i.GetType(); Console.WriteLine(type); ```
* Use strings to invoke methods: ```csharp // Without reflection var foo = new Foo(); foo.PrintHello(); // With reflection Object foo = Activator.CreateInstance ("complete.classpath.and.Foo"); MethodInfo method = foo .GetType() .GetMethod("PrintHello"); method.Invoke(foo, null); ``` ### Attributes * [Attributes](https://learn.microsoft.com/en-us/dotnet/standard/attributes/ ) can be used to extend methods, classes, or even entire programs with new ***metadata*** * Metadata is information about the types defined * Attributes use reflection so the program can examine its own metadata * Here's an example of the built-in [SerializableAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.serializableattribute?view=net-9.0). * Attribute is used by placing its name in square brackets `[]` above the declaration of the entity you want it to apply to ```csharp [Serializable] public class SampleClass { // Objects of this type can be serialized. } ``` --- * Attributes can also have parameters: ```csharp [Obsolete("Will be removed in next version.")] public static int Add(int a, int b) { return (a + b); } ``` * You can add multiple attributes either on separate lines or by separating them with a comma:
```csharp [Conditional("DEBUG")] [Conditional("TEST1")] void TraceMethod() { // ... } ```
```csharp [Conditional("DEBUG"), Conditional("TEST1")] void TraceMethod() { // ... } ```
### Custom attributes * You can write your own [custom attributes](https://learn.microsoft.com/en-us/dotnet/standard/attributes/writing-custom-attributes) by inheriting from the `Attribute` class ```csharp // This defaults to Inherited = true. public class MyAttribute : Attribute { //... } ``` * Then, you can use it like any other attribute ```csharp public class MyClass { [MyAttribute] public virtual void MyMethod() { //... } } ``` ## Dependency Injection ### The problem * Traditionally, when new objects of classes are instantiated, the consuming class handles the creation of the objects * Many classes change their functionality throughout the development of any project * This means that also _every single_ consuming class has to change * This is called *__tight coupling__* * What we want instead is [***loose coupling***](https://en.wikipedia.org/wiki/Loose_coupling), where components have little or no knowledge about separate components' definitions, and a change in one component doesn't necessitate a change in another ### The solution * What if, instead of directly creating the objects, they were provided by some interface that takes care of the creation? * This way, even if the base class changes, the consuming classes won't care because they only know about the provider * This provider is called *__Container__*, and the functionality being injected is called *__Service__* * In ASP.NET (Microsoft's framework for building web applications), this container system is built in ### Dependency injection in ASP.NET ```csharp public class HomeController : Controller { private readonly IUserRepository _userRepository; public HomeController(IUserRepository userRepository) { _userRepository = userRepository; } // User repository including all users is now accessible in HomeController } ``` ## Design Patterns * If the concepts of a singleton and dependency injection flew over your head, don't worry about it * The important thing is to know they exist so that when they come up again in ASP.NET, you have already familiarized yourself with the terms * Thus, understanding the logic behind ASP.NET becomes less overwhelming * There are many more design patterns, see the material [here](https://www.c-sharpcorner.com/UploadFile/bd5be5/design-patterns-in-net/) ```csharp public void ConfigureServices(IServiceCollection services) { services.AddSingleton(); services.AddControllersWithViews(); } ```