Discover dead code in your application using Code Analysis
Last weekend, I spent a good 30 minutes trying to figure out why I wasn't hitting a breakpoint in a method. Admittedly, I wasn't very familiar with the code since I hadn't written it. It turned out that the method wasn't being called from anywhere, so it was essentially "dead code". I wanted to remove the method in order to clean up the code but I wasn't 100% sure whether I would break anything. The only option was to use trial and error which meant that I commented out the method, rebuilt the code and ran through the scenario to verify that that the method was not being used. However, I still had a nagging feeling that maybe the code was being called through some other mechanism.
Well, it turns out that you can use Code Analysis to take out much of the guess work with it comes to dealing with dead code. But first, a definition is in order.
What is "dead code" anyway?
There are several definitions of dead code including i) redundant code ii) unreachable code and iii) unused code. Code Analysis in Visual Studio Team System can tell you about the following types of dead code:
- Private methods that are not called from any other code (CA1811)
- Unused local variables (CA1804)
- Unused private fields (CA1823)
- Unused parameters (CA1801)
- Internal classes that are not instantiated from any other code (CA1812)
In the sections below, I'm going to explain each of these types of dead code using real world examples.
Private methods that are not called from any other code (CA1811)
This type of dead code is pretty easy to understand. If you have a private method that is not called from anywhere inside your class, Code Analysis will warn you about it, as shown here:
warning : CA1811 : Microsoft.Performance : 'SearchData.NotifyPropertyChanged(string)' appears to have no upstream public or protected callers.
In order to fix this issue, you can either add code to call the method or remove the method entirely. In the following example from the Patient Monitoring WPF sample application, the NotifyPropertyChanged private method is not called from any other code.
1: private void NotifyPropertyChanged(string propName)
2: {
3: if (PropertyChanged != null)
4: {
5: PropertyChanged(this, new PropertyChangedEventArgs(propName));
6: }
7: }
Unused local variables (CA1804)
Typically, if you declare a local variable but don't assign anything to it, the compiler will warn you about it. However, if you do assign a value to the local variable, the compiler no longer complains even if you never use the variable. Take a look at the following example from the WPF Patient Monitoring sample application which declares and initializes the local variable dob but never uses it. This could be a real bug that should be investigated further.
1: public SeriesDataItem(SeriesDataItems itemParent, double itemValue)
2: {
3: _itemParent = itemParent;
4: DependencyObject dob = new DependencyObject();
5: this.SetValue(SeriesDataItem.ValueProperty, itemValue);
6: }
Code Analysis will warn you about this type of dead code:
warning : CA1804 : Microsoft.Performance : 'SeriesDataItem.SeriesDataItem(SeriesDataItems, double)' declares a variable, 'dob', of type 'DependencyObject', which is never used or is only assigned to. Use this variable or remove it.
Unused private fields (CA1823)
In the example below, the field _filename is declared and initialized but it's not used anywhere in the assembly. Code Analysis will warn the developer with the following warning and also provides a suggestion as how to resolve the issue.
warning : CA1823 : Microsoft.Performance : It appears that field 'PatientVitals._filename' is never used or is only ever assigned to. Use this field or remove it.
This type of warning could be a bug in the code that should be investigated further.
1: public class PatientVitals
2: {
3: private readonly char[] COMMA_SEPARATOR = new char[] { ',' };
4: private Dictionary<string, Waveform> _waveforms = new Dictionary<string, Waveform>();
5: private string _filename;
6:
7: public PatientVitals(string filename)
8: {
9: this._filename = filename;
10: this.ReadWaveformData(filename);
11: }
12:
13: // rest of class not shown
Unused parameters (CA1801)
This type of dead code might actually be a symptom of a real bug in the code. In the following example, the parameter nextValue is not used anywhere inside the method.
1: private void ScrollPoints(double nextValue)
2: {
3: DrawSeries();
4:
5: if (_chartPoints.Count == _chartPoints.Capacity)
6: {
7: _chartClock.Controller.SkipToFill();
8: _chartClock.Controller.Begin();
9: }
10: }
It's always worrisome when a parameter is passed to a method but never used. If you have this type dead code, code analysis will warn you about it. It should definitely be investigated further.
warning : CA1801 : Microsoft.Usage : Parameter 'nextValue' of 'Chart.ScrollPoints(double)' is never used. Remove the parameter or use it in the method body.
Internal classes that are not instantiated (CA1812)
This type of dead code is much less common than the other types mentioned above. If you declare an inner class but do not instantiate it, Code Analysis will warn you with the following message:
warning : CA1812 : Microsoft.Performance : 'Order.OrderItem' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static methods, consider adding a private constructor to prevent the compiler from generating a default constructor.
In the following example, the inner class OrderItem is never instantiated from the Order class.
1: public class Order
2: {
3: private List<OrderItem> orderItems = new List<OrderItem>();
4:
5: public Order()
6: {
7: }
8:
9: private class OrderItem
10: {
11: }
12: }
Habib Heydarian.