From a4274969bb9a9b5602bb1a4dacac6fd310ca24c5 Mon Sep 17 00:00:00 2001 From: borb Date: Tue, 8 Jul 2025 15:47:52 +0300 Subject: [PATCH] add reflection and attributes --- 15-design-patterns-in-csharp-slides.html | 136 ++++++++++++++++++++--- 15-design-patterns-in-csharp.md | 113 ++++++++++++++++++- 2 files changed, 235 insertions(+), 14 deletions(-) diff --git a/15-design-patterns-in-csharp-slides.html b/15-design-patterns-in-csharp-slides.html index 1c3d613..190ffa0 100644 --- a/15-design-patterns-in-csharp-slides.html +++ b/15-design-patterns-in-csharp-slides.html @@ -13,20 +13,21 @@ /* buutti.css */ /* @theme buutti */div#\:\$p>svg>foreignObject>section .columns{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns12{display:grid;grid-template-columns:1fr 2fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns21{display:grid;grid-template-columns:2fr 1fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns32{display:grid;grid-template-columns:3fr 2fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns23{display:grid;grid-template-columns:2fr 3fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns111{display:grid;grid-template-columns:1fr 1fr 1fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .centered{display:flex;flex-direction:column;justify-content:center;text-align:center}div#\:\$p>svg>foreignObject>section .tableborderless td,div#\:\$p>svg>foreignObject>section th{border:none!important;border-collapse:collapse}div#\:\$p>svg>foreignObject>section.extra{background-color:#5d275d;background-image:linear-gradient(to bottom,#401a40,#1d0c1d);color:white}div#\:\$p>svg>foreignObject>section.extra a{color:rgb(145,255,209)}div#\:\$p>svg>foreignObject>section.exercise{background-color:#29366f;background-image:linear-gradient(to bottom,#20636a,#173742);color:white}div#\:\$p>svg>foreignObject>section.exercise a{color:rgb(211,173,255)} -/* @theme 1ysjiehhorul1p4de1exx6vqige0dfz5fvqkoxyrp95 */div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]{columns:initial!important;display:block!important;padding:0!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:before,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:before{display:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]{all:initial;display:flex;flex-direction:row;height:100%;overflow:hidden;width:100%}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container][data-marpit-advanced-background-direction=vertical]{flex-direction:column}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split]>div[data-marpit-advanced-background-container]{width:var(--marpit-advanced-background-split,50%)}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split=right]>div[data-marpit-advanced-background-container]{margin-left:calc(100% - var(--marpit-advanced-background-split, 50%))}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure{all:initial;background-position:center;background-repeat:no-repeat;background-size:cover;flex:auto;margin:0}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure>figcaption{position:absolute;border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;white-space:nowrap;width:1px}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content],div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo]{background:transparent!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo],div#\:\$p>svg[data-marpit-svg]>foreignObject[data-marpit-advanced-background=pseudo]{pointer-events:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background-split]{width:100%;height:100%}
+/* @theme l88lgdm6g50jnf1zbzajbvpkez0urw5hs65fwufujn */div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]{columns:initial!important;display:block!important;padding:0!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:before,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:before{display:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]{all:initial;display:flex;flex-direction:row;height:100%;overflow:hidden;width:100%}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container][data-marpit-advanced-background-direction=vertical]{flex-direction:column}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split]>div[data-marpit-advanced-background-container]{width:var(--marpit-advanced-background-split,50%)}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split=right]>div[data-marpit-advanced-background-container]{margin-left:calc(100% - var(--marpit-advanced-background-split, 50%))}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure{all:initial;background-position:center;background-repeat:no-repeat;background-size:cover;flex:auto;margin:0}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure>figcaption{position:absolute;border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;white-space:nowrap;width:1px}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content],div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo]{background:transparent!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo],div#\:\$p>svg[data-marpit-svg]>foreignObject[data-marpit-advanced-background=pseudo]{pointer-events:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background-split]{width:100%;height:100%}

Design Patterns in C#

-
+

Overview

  • The Singleton Pattern
  • -
  • Dependency Injection
  • +
  • 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 @@ -44,7 +45,7 @@
-
+

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
  • @@ -56,7 +57,7 @@
-
+

A singleton implementation could look something like this:

@@ -97,17 +98,126 @@
-
+

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:
    int i = 42;
    +Type type = i.GetType();
    +Console.WriteLine(type);
    +
    +
  • +
+
+
+
    +
  • Use strings to invoke methods:
    // 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 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.
  • +
  • Attribute is used by placing its name in square brackets [] above the declaration of the entity you want it to apply to
    [Serializable]
    +public class SampleClass
    +{
    +    // Objects of this type can be serialized.
    +}
    +
    +
  • +
+
+
+
    +
  • +

    Attributes can also have parameters:

    +
    [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:

    +
  • +
+
+
+
[Conditional("DEBUG")]
+[Conditional("TEST1")]
+void TraceMethod()
+{
+    // ...
+}
+
+
+
+
[Conditional("DEBUG"), Conditional("TEST1")]
+void TraceMethod()
+{
+    // ...
+}
+
+
+
+
+
+

Custom attributes

+
    +
  • You can write your own custom attributes by inheriting from the Attribute class
    // This defaults to Inherited = true.
    +public class MyAttribute : Attribute
    +{
    +    //...
    +}
    +
    +
  • +
  • Then, you can use it like any other attribute
    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
  • @@ -119,7 +229,7 @@
-
+

The solution

  • What if, instead of directly creating the objects, they were provided by some interface that takes care of the creation?
  • @@ -128,7 +238,7 @@
  • In ASP.NET, this container system is built in
-
+

Dependency injection in ASP.NET

public class HomeController : Controller
 {
@@ -142,7 +252,7 @@
 }
 
-
+

Design Patterns

  • If the concepts of a singleton and dependency injection flew over your head, don't worry about it
  • diff --git a/15-design-patterns-in-csharp.md b/15-design-patterns-in-csharp.md index a241372..53d573a 100644 --- a/15-design-patterns-in-csharp.md +++ b/15-design-patterns-in-csharp.md @@ -14,6 +14,7 @@ title: 15. Design Patterns in C# ## Overview * The Singleton Pattern +* Reflection and attributes * Dependency Injection ## The Singleton Pattern @@ -82,12 +83,122 @@ class Program - ### 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