One Refactoring to rule them all,
– J.R.R. Tolkien, The Fellowship of the Ring + my edits in bold
One Refactoring, that’s method extract,
One Refactoring to bring them all
and in the darkness bugs feel trapped.
Do you hate long methods that are hard to read and understand? Methods where bugs are hidden but ready to be discovered by the customers?
If you do, you are not alone.
Nobody likes them.
There are a lot of ways to improve code in an existing application. One of the most common and straightforward refactorings available is to extract a method.
So, what is an extract method refactoring?
Extract Method refactoring is a process of improving the structure of code by creating a new method from an existing code block, and replacing the original code block with a call to the new method. This improves the readability and maintainability of the code.
This refactoring is a safe and easy way to break a method into smaller ones, making code more readable and easier to maintain.
And there is less chance that a bug will hide in a small method.
Explanation of the C# Extract Method Refactoring
The Extract Method is a refactoring that split one large method into smaller methods. It is used to make our code more readable, more manageable, and easier to maintain. A single long method can often be split into smaller methods, making your code easier to read.
Refactoring is an approach to improving the design of a codebase. The goal of refactoring is to improve the design without changing the external behavior of the code. A series of safe, small changes can add up to a significant improvement in code quality.
How do you know what code to extract?
Well, in a long method, identify the block of code that does a single thing. That block of code is a good candidate to perform the method extraction process.
The other important thing to mention is how to name new methods. You need to name a new method by its intention. Look at what the method does, not how it does something.
Why should you extract methods in C#?
Long methods are bad for a lot of reasons.
Not only long methods can be hard for you to read and understand what they do, but also it can be hard to test and maintain a very long method. Extracting a new method is a great way to solve problems like these.
In short, extracting methods makes our code easier to read, understand and test. A method is a unit of code that performs a specific task. Methods are easier to understand than big chunks of code. Once you perform the Extract Method refactoring, the new method will likely perform one task.
The other reason to perform Extract Method refactoring in C# is to get rid of duplication.
Question: what do you do when you have to reuse 95% of some code and perform small changes to it to fit in another place? The chances are that you copy and paste the original code, make the necessary changes, and you are done. A better approach could be to take the original code, extract it into a new method, and call it from both places, the old code, and the new code. If you have some variations, you can add a parameter to the method to solve different flows within the method.
Extract Method refactoring is a simple and straightforward refactoring that can be applied to a wide variety of situations and is easy to learn.
Benefits of C# Extract Method refactoring
The main benefits of the Extract Method refactoring are:
- It reduces duplication
- It makes your code more readable
- It makes your code more maintainable
- It makes your code more testable
In summary, this refactoring is one of the easiest things you can do to improve any C# code.
How to perform Extract Method refactoring automatically using Visual Studio 2022
The actual process of extracting a method is quite simple.
You need to identify the block of code you plan to extract and then use Visual Studio’s built-in tool for extracting methods.
How to perform the Extract Method using Visual Studio?
1. Select the code lines you want to put in a new method.
2. Click Ctrl + . and select Extract Method. Alternatively, you can right-click with the mouse and choose Quick Actions and Refactorings… and select Extract Method.
3. Enter a name for a new method.
The method name should express what the method does, not how it does it. In other words, enter a name for a method based on its intent.
The following gif shows Extract Method refactoring in action:
Couple of things to note about what will happen to the code:
- The code will be moved to a new method
- Any local variable will be passed to a new method as the method parameter
- Global variables will be used in the new method but not passed as parameters
- If a parameter is used outside of the method, it may be passed as a ref parameter
The best way to learn and master refactoring is to do it. That way, you will understand the situation well, and you will learn the requirements.
How to perform Extract method refactoring manually – mechanics
90% of the time, Visual Studio can do the heavy lifting for you and extract a new method automatically.
However, in case you have a return statement that has a return statement, Visual Studio doesn’t know what you want to have in place where you call the new extracted method. For example, let’s say you want to extract the if block to a new method from the Index method:
public ActionResult Index()
{
if (CurrentSession.GetCurrentSession() != null &&
_iCommonManager.HasAccessPermission(getActionUrl()) > 0)
{
Session["CapturedImage"] = "";
Session["CapturedImageName"] = "";
return View();
}
return RedirectToAction("Index", "Login");
}
In that case, Visual Studio doesn’t know how to formulate the resulting code. The straightforward replacement will have some issues.
public ActionResult Index()
{
CanAccessCurrentPage();
return RedirectToAction("Index", "Login");
}
private ViewResult CanAccessCurrentPage()
{
if (CurrentSession.GetCurrentSession() != null &&
_iCommonManager.HasAccessPermission(getActionUrl()) > 0)
{
Session["CapturedImage"] = "";
Session["CapturedImageName"] = "";
return View();
}
}
There are two issues:
CanAccessCurrentPage
method is incomplete and doesn’t compile – it’s missing a return statement after the if statement- The logic in the
Index
method is wrong – even if the user has permission to view the current page, the code will always redirect him to theLogin
page.
The solution? You have to perform the Extract Method manually and tweak it so that it still works after the refactoring.
The steps are:
- Create a new method. The name of the method should explain what the method does.
- Copy the selected code statements to the new method.
- Are there any local variables in the extracted code? These variables become parameters and local variables in the new method.
- Are there any temporary variables used only in the extracted code? If there are, you need to define them as temporary variables inside the new method.
- Is any of the local variables from the original method modified in the extracted code? If so, see if you can change the method, so that it returns the change as the returning parameter. If there are more of these variables, you can use Tuple or define a new class that will return more variables as properties.
- In the original method, replace the extracted code with the call to the new method.
- Compile and test.
Extract Method C# Example
I will use the example from my Clean Code Commando course.
In case you prefer a video version, I have also included the video.
You are going to see how to refactor a long method that performs a search for a smartphone.
//list of available smartphone results
List<string> smartphones = new List<string>()
{
"Samsung Galaxy S20",
"Pixel 2",
"Pixel 3",
"Pixel 4",
"iPhone XR",
"iPhone 12",
"iPhone 12 Pro",
"iPhone 12 Pro Max"
};
//long method that we will refactor
public void PerformSearch()
{
bool continueSearch = true;
while (continueSearch)
{
//user enters the term
Console.Write("Search for smartphone: ");
string keyword = Console.ReadLine();
var results = smartphones
.Where(phone => phone
.ToLower()
.Contains(keyword.ToLower()));
//if there are resuls, they are displayed in the output
if (results != null)
{
Console.WriteLine("Here are the matched results.\n");
foreach (var result in results)
{
Console.WriteLine(result);
}
}
else
{
Console.WriteLine("No results found.");
}
//this asks user if he wants to search again
//valid responses are Y and N
//the program stops if the answer is N
string continueSearchResponse;
do
{
Console.Write("\nMake another search (y/n)?: ");
continueSearchResponse = Console.ReadLine();
if (continueSearchResponse.ToLower() == "n")
{
continueSearch = false;
break;
}
if (continueSearchResponse.ToLower() != "y")
{
Console.WriteLine("Invalid response.");
}
} while (continueSearchResponse.ToLower() != "n"
&& continueSearchResponse.ToLower() != "y");
}
Console.Write("Thanks for searching!");
}
This is a very long method and it does several things:
- It takes the user’s input
- Searches for results
- Displays the results to the console output
- Checks if the user wants to search again
We can simplify the code with the Extract Method.
The first refactoring is to extract the part of the code that performs a search for smartphones. Let’s extract that block of code into a new method.
private void SearchForSmartphones()
{
//user enters the term
Console.Write("Search for smartphone: ");
string keyword = Console.ReadLine();
var results = smartphones
.Where(phone => phone
.ToLower()
.Contains(keyword.ToLower()));
//if there are resuls, they are displayed in the output
if (results != null)
{
Console.WriteLine("Here are the matched results.\n");
foreach (var result in results)
{
Console.WriteLine(result);
}
}
else
{
Console.WriteLine("No results found.");
}
}
And the initial method is now shorter.
public void PerformSearch()
{
bool continueSearch = true;
while (continueSearch)
{
//new method is called from this line
SearchForSmartphones();
//this asks user if he wants to search again
//valid responses are Y and N
//the program stops if the answer is N
string continueSearchResponse;
do
{
Console.Write("\nMake another search (y/n)?: ");
continueSearchResponse = Console.ReadLine();
if (continueSearchResponse.ToLower() == "n")
{
continueSearch = false;
break;
}
if (continueSearchResponse.ToLower() != "y")
{
Console.WriteLine("Invalid response.");
}
} while (continueSearchResponse.ToLower() != "n"
&& continueSearchResponse.ToLower() != "y");
}
Console.Write("Thanks for searching!");
}
The second step is to move to a new method the code that asks if the user wants to perform another search.
private static bool ShouldContinueWithSearch(bool continueSearch)
{
//this asks user if he wants to search again
//valid responses are Y and N
//the program stops if the answer is N
string continueSearchResponse;
do
{
Console.Write("\nMake another search (y/n)?: ");
continueSearchResponse = Console.ReadLine();
if (continueSearchResponse.ToLower() == "n")
{
continueSearch = false;
break;
}
if (continueSearchResponse.ToLower() != "y")
{
Console.WriteLine("Invalid response.");
}
} while (continueSearchResponse.ToLower() != "n"
&& continueSearchResponse.ToLower() != "y");
return continueSearch;
}
This leaves us with a very slim PerformSearch method.
public void PerformSearch()
{
bool continueSearch = true;
while (continueSearch)
{
SearchForSmartphones();
continueSearch = ShouldContinueWithSearch(continueSearch);
}
Console.Write("Thanks for searching!");
}
After this, we are done with method extractions. The next steps in this refactoring would be to analyze the new methods and see if it’s possible to simplify the code even further.
How to make sure the refactoring is done correctly
No matter how good your code is, it has to be maintained.
If you don’t have tests, it will be harder to make sure that your refactorings don’t turn into regressions. However, if you use Visual Studio to create a new method for you automatically, then the chance of introducing a bug is almost non-existent.
My advice is to cover any methods you plan to change with tests. I also have some useful posts on how to write your first test.
Refactoring with the Extract Method, is it always a good idea?
The Extract Method is often considered one of the simplest refactorings, but it’s also one of the most useful. It can help you refactor duplicated code into a single method, and it can help improve the structure of your program.
However, it might take you a while to get used to the new code with small methods. Especially if you are used to having long, never-ending methods.
Otherwise, the extract method is 99% of the time a good idea.
Conclusion
Extract Method in C# is a refactoring operation that you can use to reduce complexity for methods that are too long, have too many responsibilities, or just don’t make sense the way they are. Although the act of extracting a method can seem daunting at first, it can in fact be an easy and fun thing to do.
I have been programming for a little over 10 years now and as I continue learning and growing as a programmer, I become more and more aware of the importance of code quality. I believe that the code that you write is a reflection of you and your skills. Today you have learned a powerful skill. A skill that will instantly make your code more organized. And make you a better developer.
FAQ
How to invoke the extract method command?
To invoke the Extract Method command inside the Visual Studio, use the Quick Actions and Refactorings context menu option. You can also use the Ctrl + R, then Ctrl + M shortcut to involve the Extract Method refactoring.
How do you know what code to extract?
You can use the Extract Method refactoring when you have a code fragment that can be grouped together. This can reduce the number of lines in the original method. This refactoring will extract the code fragment into a separate method, which can then be called from the original method.
The other use case is when you have a code block that is repeated multiple times throughout your code. This way, you can extract the code block and put it in a method, and then simply call that method whenever you need to execute the code.
What if you apply the Extract Method, and you are not satisfied with the results?
The solution is simple. You can revert this refactoring by using the Inline Method refactoring. Inline method refactoring is a handy refactoring technique that allows you to replace a method call with the actual code of the method in the place where it is called.