Earn More by Sharing What You Love
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Tim Corey
53m 20s
Design patterns in C# are essential tools for writing efficient, reusable, and maintainable code. These patterns provide standard solutions to common software design problems, promoting best practices and helping developers avoid redundant code. One of the core principles in applying design patterns is the DRY (Don't Repeat Yourself) principle, which emphasizes minimizing repetition within code to enhance readability and maintainability.
This article is inspired by Tim Corey's insightful video, "Design Patterns: Don't Repeat Yourself in C#," which dives deep into the DRY principle and its practical application in creating cleaner, more organized code. By exploring the key concepts and strategies discussed in Tim's video, this article aims to provide you with a comprehensive guide to implementing the DRY design pattern principle effectively in your C# projects.
In the introduction, Tim Corey explains the DRY principle, which stands for "Don't Repeat Yourself." This principle is a fundamental concept in programming that emphasizes avoiding redundancy by ensuring that every piece of knowledge or logic is represented in a single place in the code. Tim illustrates the principle using a simple example of a WinForms application with a dashboard form. The form includes fields for entering a first name and a last name, and a button to generate an employee ID based on these fields.
At (0:53), Tim moves on to identifying and anticipating repetition in the code. He uses the example of the WinForms application to show how repetition can occur, even when methods are called only once. In the application, the employee ID generation logic involves extracting substrings from the text fields for the first and last names and appending a 3-digit code at the end.

In the above screenshot at (1:31), Tim demonstrates the functionality of the application, showing how it generates an employee ID by combining the first four letters of the first name and last name with a three-digit code. He highlights that, although the code appears to follow the DRY principle because it doesn't repeat the same logic explicitly, there are underlying issues with the pattern of repetition that need to be addressed.
At (1:51), he points out that while the code seems simple, it doesn't fully adhere to the DRY principle because the logic for generating the employee ID is tightly coupled with the click event of the button. This means that if this logic were needed elsewhere in client code, such as when processing a list of new employees (3:58), the code would need to be repeated or adapted, leading to redundancy.
In this segment, Tim Corey demonstrates how to create an independent, reusable method to adhere to the DRY principle. He begins by extracting the logic for generating an employee ID from the event handler into a separate method. This refactoring involves creating a private method named GenerateEmployeeID and moving the existing code into this method (5:15). The revised code in the event handler then simply calls this method.
Initial Code: The logic for generating the employee ID was directly in the click event handler of a button.

Refactored Code: Tim improves the method by making it more flexible. Instead of relying on specific UI elements, the method now accepts firstName and lastName as parameters and returns the generated ID. This change allows the method to be used in various contexts and UI elements:
private string GenerateEmployeeID(string firstName, string lastName)
{
string employeeID = firstName.Substring(0, 4) + lastName.Substring(0, 4) + DateTime.Now.Millisecond.ToString();
return employeeID;
}
Tim then demonstrates how this method is called from the click event:
employeeIdText.Text = GenerateEmployeeID(firstNameText.Text, lastNameText.Text);employeeIdText.Text = GenerateEmployeeID(firstNameText.Text, lastNameText.Text)He also notes that this method can now be used in other parts of the application, such as in processing CSV files with multiple employee records, without repeating the code.
Tim Corey then explores the concept of a class library to further enhance code reuse and maintainability. He illustrates how to encapsulate the GenerateEmployeeID method into a class library object, which can be used across multiple projects.
At (8:00), Tim explains that the design keeps on changing based on the requirements of the user or by company policies to make it more interactable with graphics and animations. So, he introduces a WPF project within the solution with exact fields and a button to Generate Employee ID.
Tim at (9:15), makes a strong case for using a class library by saying if we are to avoid repeating ourselves, then the code would have been copy-pasted in the new WPF project. So, to keep it DRY we need to create classes in a class library.
Creating the Class Library:
Tim at (9:47), creates a new class library project in .NET Framework, naming it DRYDemoLibrary.
Inside this library, he defines a public class EmployeeProcessor and moves the GenerateEmployeeID method into this class:
public class EmployeeProcessor
{
public string GenerateEmployeeID(string firstName, string lastName)
{
string employeeID = firstName.Substring(0, 4) + lastName.Substring(0, 4) + DateTime.Now.Millisecond.ToString();
return employeeID;
}
}
Using the Class Library in Projects:
In his WinForms (13:18) and WPF projects (14:00), Tim adds a reference to the DRYDemoLibrary class library.
He then replaces the old code with calls to the GenerateEmployeeID method from the class library:
EmployeeProcessor processor = new EmployeeProcessor();
employeeIDText.Text = processor.GenerateEmployeeID(firstNameText.Text, lastNameText.Text);
This approach eliminates redundancy, as the method is now maintained in a single place. Tim demonstrates that the same class library can be used across different UI frameworks (WinForms and WPF) without repeating the code.
Advantages:
Tim Corey continues to explore how to use the DRYDemoLibrary class library in different types of projects, specifically focusing on integrating the library into a new console application. This demonstrates how the library's functionality can be reused across various applications, not only a single instance or just those within the same solution.
Creating a New Solution and Project:
Tim at (17:29), starts by creating a new solution for a console application, simulating a scenario where you might need to use the DRYDemoLibrary in a different type of project, like a Windows service or console app.
He names the new project ConsoleUI and shows how to set up a basic console application.
class Program
{
static void Main(string[] args)
{
Console.ReadLine();
}
}
Adding a Reference to the Class Library:
Tim explains how to add a reference to the DRYDemoLibrary DLL in the new project. This involves browsing to the DLL file in the bin folder of the class library project and adding it to the console application.
using DRYDemoLibrary;Imports DRYDemoLibraryOnce the reference is added, Tim (19:24) uses the EmployeeProcessor class from the library to generate an employee ID based on user input.
Console.WriteLine("What is your first name?");
string firstName = Console.ReadLine();
Console.WriteLine("What is your last name?");
string lastName = Console.ReadLine();
EmployeeProcessor processor = new EmployeeProcessor();
string employeeID = processor.GenerateEmployeeID(firstName, lastName);
Console.WriteLine($"Your employee ID is {employeeID}");
Running the Console Application:
Tim demonstrates running the console application to show that it successfully generates the employee ID using the library. This confirms that the same code from the class library can be reused across different projects.

Updating the DLL:
Tim Corey briefly introduces the concept of using NuGet packages for managing and updating class libraries. This approach offers a more scalable solution for handling dependencies and updates, especially in larger projects or organizations.
Creating a NuGet Package:
Updating Packages:
Benefits:
In this segment, Tim Corey demonstrates how applying the DRY (Don't Repeat Yourself) principle can enhance unit testing. He shows how to implement DRY principles in development work, especially focusing on unit tests.
Tim begins by running a unit test that currently fails due to a bug in the DLL. He highlights the importance of unit tests in identifying problems, even when the code is outside the main solution. The code was expecting a 4-letter input but instead Tim passed a 3-letter first name which crashes in the DLL file even if it's not directly included in the solution.

To address the issue with first name handling, Tim refactors the code. He explains how DRY can be applied to development by creating a new class library project (23:50). This approach ensures that changes to multiple objects can be made once and tested effectively without repeating fixes.

Tim introduces a new test class as EmployeeProcessorTest in the class library project and sets up unit tests using XUnit. He demonstrates how to create a test method for generating employee IDs and discusses the importance of mocking dependencies instead of relying on actual values.

Tim writes a unit test method called GenerateEmployeeID_ShouldCalculate. He sets up a theory with inline data to test different scenarios, ensuring the method returns the expected results. He also explains how to use Assert.Equal to verify the output.
public class EmployeeProcessorTest
{
[Theory]
[InlineData("Timothy", "Corey", "TimoCore")]
public void GenerateEmployeeID_ShouldCalculate(string firstName, string lastName, string expectedStart)
{
// Arrange
var processor = new EmployeeProcessor();
// Act
var actualStart = processor.GenerateEmployeeID(firstName, lastName).Substring(0, 8);
// Assert
Assert.Equal(expectedStart, actualStart);
}
}Public Class EmployeeProcessorTest
<Theory>
<InlineData("Timothy", "Corey", "TimoCore")>
Public Sub GenerateEmployeeID_ShouldCalculate(firstName As String, lastName As String, expectedStart As String)
' Arrange
Dim processor = New EmployeeProcessor()
' Act
Dim actualStart = processor.GenerateEmployeeID(firstName, lastName).Substring(0, 8)
' Assert
Assert.Equal(expectedStart, actualStart)
End Sub
End ClassTim emphasizes the importance of mocking dynamic data, like date-time values, to control test conditions and outcomes. He discusses the challenge of working with dynamic strings and how to test different scenarios using controlled values. He then runs the unit test but before this he adds two NuGet packages that are necessary to run the tests: xunit.runner.console and xunit.runner.visualstudio.

After successfully running all the tests for one inline data, the output is shown as follows:

Now at (31:30), Tim added another inline data and changed the substring second parameter to expectedStart.Length:
public class EmployeeProcessorTest
{
[Theory]
[InlineData("Timothy", "Corey", "TimoCore")]
[InlineData("Tim", "Corey", "TimCore")]
public void GenerateEmployeeID_ShouldCalculate(string firstName, string lastName, string expectedStart)
{
var processor = new EmployeeProcessor();
var actualStart = processor.GenerateEmployeeID(firstName, lastName).Substring(0, expectedStart.Length);
Assert.Equal(expectedStart, actualStart);
}
}Public Class EmployeeProcessorTest
<Theory>
<InlineData("Timothy", "Corey", "TimoCore")>
<InlineData("Tim", "Corey", "TimCore")>
Public Sub GenerateEmployeeID_ShouldCalculate(firstName As String, lastName As String, expectedStart As String)
Dim processor = New EmployeeProcessor()
Dim actualStart = processor.GenerateEmployeeID(firstName, lastName).Substring(0, expectedStart.Length)
Assert.Equal(expectedStart, actualStart)
End Sub
End ClassAfter running the unit test again at (32:05), with second theory the test broke:

To adhere to DRY, Tim refactors the code further by creating a private method GetPartOfName in the actual EmployeeProcessor class under DRYDemoLibrary. This method handles the extraction of parts of a name, improving code reusability and readability. Tim made the following changes:
public string GenerateEmployeeID(string firstName, string lastName)
{
string employeeID = $@"{GetPartOfName(firstName, 4)}{GetPartOfName(lastName, 4)}{DateTime.Now.Millisecond.ToString()}";
return employeeID;
}
private string GetPartOfName(string name, int numberOfCharacters)
{
string output = name;
if (name.Length > numberOfCharacters)
{
output = name.Substring(0, numberOfCharacters);
}
return output;
}Public Function GenerateEmployeeID(ByVal firstName As String, ByVal lastName As String) As String
Dim employeeID As String = $"{GetPartOfName(firstName, 4)}{GetPartOfName(lastName, 4)}{DateTime.Now.Millisecond.ToString()}"
Return employeeID
End Function
Private Function GetPartOfName(ByVal name As String, ByVal numberOfCharacters As Integer) As String
Dim output As String = name
If name.Length > numberOfCharacters Then
output = name.Substring(0, numberOfCharacters)
End If
Return output
End FunctionTim updates the unit tests to reflect changes in the code, such as modifying the expected length of substrings. He explains how running these tests helps quickly identify issues and validate that the code meets the new requirements. Tim adds new theories and then run the unit tests to verify if the outputs are expected:

To enhance the versatility of your class library, Tim Corey recommends transitioning from a .NET Framework class library to a .NET Standard class library. This change allows the library to be compatible across various platforms, including:
Add New Project: Right-click on your solution and choose to add a new project.
Select .NET Standard: Instead of selecting a .NET Framework class library, choose .NET Standard. This library type supports a wide range of platforms.

Code Migration: Copy and paste your existing code (e.g., EmployeeProcessor class) into the new .NET Standard library. This process may involve minor adjustments, but the core logic remains consistent.
By converting to .NET Standard, you make your library accessible from various platforms, reducing code repetition across different application types and saving development effort.
Tim Corey emphasizes that by adopting a .NET Standard library, you minimize code repetition not just in your codebase, but also in the development process. Instead of duplicating code across different platform-specific projects, you centralize it in a single library that works across multiple environments.
Testing and Debugging: Tim introduces unit testing as a way to further reduce effort and repetition. Automated tests verify your code's correctness without needing to manually test each application iteration.
Tim Corey emphasizes that while following the DRY (Don't Repeat Yourself) principle is crucial for writing maintainable code, it's important to know when and where to apply it. Not every scenario requires the same approach, so here are some practical tips inspired by Tim's insights:
By following these tips, you can apply the DRY principle effectively while balancing the need for code reuse and maintainability with practical considerations.
Mastering the DRY principle through design patterns is essential for writing clean and maintainable C# code. As demonstrated by Tim Corey, applying DRY effectively involves creating reusable methods, leveraging class libraries, and embracing .NET Standard for broader compatibility. By understanding when and how to apply these practices, you can significantly enhance the quality and flexibility of your code.
For more in-depth insights, check out Tim Corey's video on this topic here. To stay updated with Tim's latest content, visit his YouTube channel.
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!
Join our newsletter, you’ll get exclusive access on article updates. We value your privacy