VSLive! Blog

Industry Insights, Information, and Developer News

Blog archive

Why Use It? Understanding C# Interfaces, Delegates and Dependency Injection

A working knowledge of C# can take you a long way -- but many developers hit a wall when it comes to truly understanding why they should use certain language features and design patterns. It's one thing to know how interfaces or dependency injection work in principle. It's another to understand their practical value in writing maintainable, testable, and adaptable code. And that "why" is often what separates an intermediate developer from an advanced one.

That's the core focus of Jeremy Clark's full-day, hands-on lab, Take Your C# Skills to the Next Level, taking place at Live! 360 Orlando this November. Clark, a longtime developer educator and founder of JeremyBytes.com, has designed this full-day session to help attendees move beyond surface-level understanding of key C# concepts -- interfaces, delegates, unit testing, and dependency injection -- by digging into their real-world benefits.

"In this workshop, we will cover the 'how' of these technologies, but more importantly, you will learn the 'why' of the technologies so that you can use them effectively. And this can take you to the next level as a developer."
-- Jeremy Clark, Developer Educator, JeremyBytes.com

Through guided exercises and applied problem-solving, attendees will explore how these tools interrelate and how mastering them can improve flexibility, reduce code duplication, simplify testing, and make applications more responsive to change. The lab format ensures that developers not only see these concepts in action but get hands-on experience applying them in realistic coding scenarios -- like replacing hard-coded dependencies or injecting functionality through delegates.

Ahead of the conference, we caught up with Clark to talk about the thinking behind the session, why developers often struggle with abstraction, and how understanding the why can elevate your code and your confidence.

Inside the Session

What: Hands-On Lab: Take Your C# Skills to the Next Level

When: Nov. 16, 2025, 9 a.m. - 6 p.m.

Who: Jeremy Clark, Developer Educator, JeremyBytes.com

Why: Learn how interfaces, delegates, dependency injection, and unit testing can help you write applications that are easy to extend, change, and debug.

Find out more about about Live! 360 Orlando taking place Nov. 16-21.

VisualStudioMagazine: What inspired you to present a session on this topic?
Clark: I approached development much differently after I had a solid understanding of why we use particular technologies or objects in C#. For example, I understood the mechanics of how interfaces worked, but I didn't really get why I would want to use them in my code. Once I understood the why, I became much more effective at writing good, solid software for my users. I know that other developers also have difficulty understanding the "why."

This is also true of the other topics in this workshop: dependency injection, delegates, and unit testing. And once you understand why you want to use these techologies and patterns, you find that they are all interrelated: they all can make software easier to build, maintain, and test. The reason I put this workshop together is to help developers get past some of the hurdles I experienced back when I was trying to understand the "why."

In this workshop, we will cover the "how" of these technologies, but more importantly, you will learn the "why" of the technologies so that you can use them effectively. And this can take you to the next level as a developer.

What's a concept you see intermediate C# developers struggle with most often?
I am often asked why we want to use certain technologies. "Why would we add an interface here? It seems like that abstraction makes things more complicated." It's true that when we add abstraction, we increase the complexity of the code. But that complexity is minor compared to the benefits that we can get.

An interface does add a layer of indirection; but because of that indirection, we can more easily swap out functionality when business needs change. We can add new functionality more easily (sometimes without modifying any existing objects). And we can make unit tests easier to read and easier to write.

Convoluted unit tests cause problems: when something goes wrong, a developer is tempted to simply comment out the test rather than try to figure out what went wrong. And developers are reluctant to write new tests if the test code is difficult to write. When you have straight-forward tests, then when something goes wrong, you are more likely to look into the errors and determine whether the code needs to change or the test needs to change. And if tests are easy to read, you are more likely to write more tests when needed.

How can interfaces make code more flexible and easier to test?
Interfaces represent a set of functionality. And with interfaces, you can write code against just the piece of functionality that you need. For example, if all you need is to iterate over items with a "foreach" loop, then you can code against the "IEnumerable" interface rather than a more specific type. Here's an example:

IEnumerable<Customer> customers = GetCustomers();
foreach(var customer in customers)
{
    UpdateLoyaltyProgram(customer);
}

Pretty much every collection in .NET implements the IEnumerable interface, so you don't need to know about the "real" type. It could be a list, a stack, a queue, an array -- it doesn't matter as long as it implements the IEnumerable interface. This also means that if the underlying type changes (because another member of the team needs different functionality), your code does not need to change as long as the new type implements the piece of functionality that you need. Your code stays flexible even when other parts of the application are changing around it.

Interfaces also give you an easy way to provide test data for unit testing. For example, if you test your objects against production data, that data constantly changes. This means that you are never sure exactly what is getting tested. Interfaces can give you a good place to swap in test objects that provide consistent data or behavior. Then you can focus on the specific class or method under test. You can have test objects that give good data, bad data, poke at the edge cases, and much more. When you come across a bug in the application, you can update the test objects to test for those cases, make sure the bug is fixed, and also make sure that you didn't break any existing functionality in the process.

What's the role of delegates in real-world application code?
Delegates let you use methods as objects. This means that they can be used as fields, parameters, and return types. Most lambda expressions that you see in C# are anonymous delegates, and these are very common in .NET libraries. Minimal APIs use delegates (often implemented with lambda expressions). Asynchronous and parallel code uses delegates (also often with lambda expressions). The LINQ fluent syntax uses delegates (also often with lambda expressions). You get the idea. One important topic in this workshop is getting a good grasp on lambda expression syntax so that you can use it easily and effectively.

But I also find custom delegates useful -- particularly when using a delegate as a parameter. When I pass a method (delegate) as a parameter to another method, I am injecting custom functionality into that method. For example, let's say that I have multiple methods with almost identical functionality -- they only differ in one or two lines of code. By using delegates, I can take all of the code that is identical and put it into a single method and then pass in the unique functionality as a parameter to that method. This has helped me reduce duplication and also make my code easier to extend. If I need slightly different functionality, instead of creating a whole new method, I pass the functionality in through the delegate parameter (often as a lambda expression). I just extended the functionality of my original method without needing to change the method itself. That's really cool.

Why do you think dependency injection is often misunderstood by newer developers?
Many times -- probably most times -- developers are introduced to dependency injection (DI) completely backwards: they are given an application that already has a DI container in it and told "Good Luck!". But why are we using dependency injection? It's really hard to tell in that scenario. In this workshop, we take a step back from the DI containers to really look at what dependency injection is (a set of principles and patterns for loose coupling) and what benefits we can get from it in our code. Once you understand the concepts of DI, you can use DI containers more effectively. Those containers do a lot of work for you, but if you do not understand it, it looks like magic. Understanding the DI concepts and patterns dispels that magic. You can understand what the DI containers do for you and use them more effectively.

What makes this lab format especially effective for skill building?
Listening to someone talk about and show examples of a technology can be helpful. But when you actually put that technology into practice, you'll find that you have a better understanding of it, and you are more likely to remember it later when you have a use for it. In this workshop, you will go hands-on with labs throughout the day to practice the skills that I show. This will further solidify the how and why for interfaces, dependency injection, and unit testing.

The application in the labs has real-world problems, and the labs walk through solving them. These include things such as needing to isolate code from hardware, swapping out a third-party service for a local class, unit testing schedules (including schedules that use DateTime.Now), and removing hard-coded references from the code. This practice helps you understand how the technologies really work.

How can attendees learn more about this topic and prepare for your session?
This workshop is designed for people who have a working knowledge of C# -- meaning you're comfortable writing C# using the development environment of your choice. I will be using Visual Studio 2022, but the labs are designed to work with Visual Studio Code and other C# editors. No specific knowledge of the workshop topics is necessary in advance. After the workshop, if you want to dive deeper into interfaces, delegates, unit testing, and dependency injection, you can refer to the workshop materials which include links to a number of articles that I have written over the years as well as articles from other experts in the field.

Note: Those wishing to attend the session can save money by registering early, according to the event's pricing page. "Save $400 when you register by the Super Early Bird deadline of Sept. 26," said the organizer of the event, which is presented by the parent company of Visual Studio Magazine.

Posted by David Ramel on 09/11/2025


Keep Up-to-Date with Visual Studio Live!

Email Address*Country*
Please type the letters/numbers you see above.