C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
94: Custom Attributes
I've noticed a recurring pattern when developers first encounter attributes. They see things like [Serializable] or [HttpPost] in ASP.NET and assume that the attribute itself is "doing" the work. There's a common belief that placing a custom attribute above a method acts like a trigger or a hook—that the attribute contains logic that automatically intercepts the execution of that method.
Attributes Aren't Magic Triggers
If you think that adding [LogExecution] to a method will suddenly make that method print its own logs to the console, you're in for a surprise. Let's look at why that's wrong. If I write this:
[LogExecution]
public void ProcessPayment()
{
// Payment logic here
}
Absolutely nothing happens. The LogExecutionAttribute class can have methods and properties, but C# never automatically calls them just because the attribute is present. If you've used frameworks like Spring in Java or certain AOP (Aspect Oriented Programming) libraries, you might be used to "magic" intercepts, but in raw C#, attributes are passive. They are just data.
Metadata is Just a Sticker
The correct way to think about attributes is as "stickers" or "tags." When you apply an attribute, you aren't adding behavior; you are adding metadata. You're essentially leaving a note for some other piece of code to find later.
To make an attribute actually do something, you need a "Processor"—a piece of code that uses Reflection to look at the tags and decide how to react. Here is how you actually build this pattern. First, you define the attribute by inheriting from System.Attribute:
[AttributeUsage(AttributeTargets.Method)]
public class DeveloperNoteAttribute : Attribute
{
public string Note { get; }
public DeveloperNoteAttribute(string note)
{
Note = note;
}
}
Now, I can tag several methods in my codebase with specific warnings or notes. But remember: these notes are invisible to the program's actual logic until we go looking for them.
Putting the Processor to Work
To make the metadata useful, you need to write the logic that reads the attribute. This usually happens at startup or via a wrapper. I like to think of this as the "Scanner" phase. Here is a simple example of a processor that scans a class for those DeveloperNote stickers and prints them out:
public class NoteProcessor
{
public void PrintNotes(object obj)
{
var type = obj.GetType();
var methods = type.GetMethods();
foreach (var method in methods)
{
var noteAttr = (DeveloperNoteAttribute)Attribute.GetCustomAttribute(method, typeof(DeveloperNoteAttribute));
if (noteAttr != null)
{
Console.WriteLine($"Method {method.Name} has a note: {noteAttr.Note}");
}
}
}
}
I'll be honest: you won't write "Processors" every day. Most of the time, you'll be using attributes provided by a framework (like Entity Framework or Newtonsoft.Json) where the framework authors have already written the complex Reflection logic for you. But understanding that the attribute is just a label and the framework is the reader is the key to mastering how C# handles metadata.
📋 Practical Task
Building a Method Permission Validator
Your goal is to create a simple permission system using custom attributes. Instead of hard-coding permission checks inside every method, you will tag methods with a required role and create a validator that checks if a user has that role before "allowing" the method to run.
- Create a custom attribute called
RequiredRoleAttributethat takes a string (the role name) in its constructor. - Create a class
SecureServicewith at least two methods: one tagged with[RequiredRole("Admin")]and another tagged with[RequiredRole("User")]. - Create a
PermissionCheckerclass with a methodbool CanExecute(object userRole, string methodName, object serviceInstance). - The
CanExecutemethod must use Reflection to:- Find the method by name on the service instance.
- Check if the
RequiredRoleAttributeis present. - Compare the role defined in the attribute with the
userRoleprovided. - Return
trueif the roles match or if no attribute is present; otherwise, returnfalse.
There are no comments for now.