📜 ⬆️ ⬇️

Removing unused assemblies from a .NET project

Sometime while studying at university, a teacher, checking a laboratory work on C ++, suddenly suddenly asked me a question: “Why do you need #include“% library_name% ”here? Can you clarify for which parts of the code each include directive is needed? ”The directive that“ caught his eye ”was added when trying to use a class. The class apparently did not take root in the laboratory and its use was safely removed, but the remained remained ...

When programming in C # using Visual Studio, we also encounter unused using directives. But Visual Studio can help deal with the problem, it’s enough for the .cs file to call the command “Remove Unused Usings”. True, there is another place that also would not hurt from time to time to clean. These are references (References) of the project. Sadly, for the C # project there is no such command. MS Connect even created a bug about this. But for VB.NET projects there is such a function (you can find it in the project properties), but by evil irony of fate for VB.NET projects there is no command to delete unused Imports (usings in C #) :)

Fueled by a thirst to make useful to colleagues, independent developers decided to write small extensions for Visual Studio. And here is also the Extension Manager from Visual Studio 2010 so simplified the process of distributing extensions. An example of such extensions can be found here and here . It is impossible to judge the algorithms used in these extensions. Although I will not hide the fact that after the first extension shamelessly removed from the project a decent part of the assemblies that were really needed for compilation, we still looked at it with a reflector ... We didn’t deal with the second anymore. In general, the problem is the same, but the key phrase can be found in the previous sentence: necessary for compilation.
')
Consider a simple example. Suppose there are 3 projects - 3 assemblies. The Assembly_A assembly defines the Class_A class, the Assembly_B assembly defines the Class_B class inherited from the Class_A class from the Assembly_A assembly. Each class has different methods, say the class_A class method is Method_A, and the class_B class method is Method_B. In the third assembly (Assembly_C) we want to use the class Class_B. To do this, we add references to Assembly_A and Assembly_B assemblies in the project, then in one of the classes we create an instance of the Class_B class, call the Method_B ​​method and compile the project. Assembly Assembly_C is ready, let's open it with ildasm.exe and take a look at the manifest:
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly extern Assembly_B
{
.ver 1:0:0:0
}
.assembly Assembly_C
{
//
}
.module Assembly_C.exe
// MVID: {B387984A-3515-4B26-9450-592FCF5FB6FA}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000003 // ILONLY 32BITREQUIRED
// Image base : 0x014C0000


* This source code was highlighted with Source Code Highlighter .

This is what happens? Assembly_A we added to the project, but it is not needed? Open Visual Studio and delete Assembly_C link for Assembly_A assembly. Compile and ... get the error “The type 'Assembly_A.Class_A' is referenced. You must add a reference to assembly 'Assembly_A, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null'. ”

It is important to understand the reason for this behavior. There is no explicit reference to Assembly_A assembly types in the project, so the reference to this assembly is not included in the project assembly manifest (Assembly_C). At the same time, one of the assembly types Assembly_B is used in the project. In fact, it turns out that for the runtime (runtime) it is enough to have a reference to the Assembly_B assembly. And the builds on which it depends, the CLR will receive from its manifest and also load it. But for the compiler (compile time) it is important to have in the Assembly_C project both references to Assembly_B and Assembly_A, because he should know everything about the class_B used, including its ancestors. A good article about build dependencies was published in MSDN Magazine, you can read it here .

It does not matter where your project uses a class: as a field of a class, as a parameter of a method, as an attribute, etc. It is important to understand that the compiler should be able to get complete information about all types used in the project. We have to clearly indicate the assembly we want to use, because the class can exist in different versions of the same assembly (even if the compiler can find the assembly (say in the GAC), how can he choose the right one if there are several of them?). This is what should be the main idea when developing a program capable of finding unused assemblies in a project, i.e. assemblies that are not required for compilation.

The study of project class dependencies serves as the basis for the Reference Assistant extension, which we developed at the Lardite Group . This is a free extension available in Visual Studio Gallery , in addition, you can download the Reference Assistant source code from the project page on CodePlex.

It was with the analysis of the class hierarchy that Reference Assistant began. Gradually, an analysis of the interface hierarchy, an analysis of attributes and types of their parameters, an analysis of imported types (for example, from a COM library), and types of objects moved to another assembly were added to it. Yes, there are those! A simple example - ObservableCollection migrated from the WindowsBase.dll assembly (fx3.5) to System.dll (fx4.0).

I like the example of analyzing overloaded methods. Suppose that in the Assembly_B assembly the class Class_B is defined, in which the SetCode method is overloaded. Let two of its overload take one parameter at a time: one of type System.Int32, the other Assembly_A.Class_A. In the project assembly (Assembly_C), one of the overloaded SetCode methods of class Class_B is called which takes one parameter. In this case, the compiler must know everything about the types of parameters of both methods in order to choose the most appropriate one. This means that assemblies with definitions of types participating in the hierarchy should be in the project links. Those. in our case, references to the Assembly_C project should contain a reference to the Assembly_A and Assembly_B assemblies. The described example is in the form of code:
// Assembly_A.dll
namespace Assembly_A
{
public class Class_A
{
public int Code { get ; set ; }
}
}

// Assembly_B.dll
using Assembly_A;
namespace Assembly_B
{
public class Class_B
{
public void SetCode( int code)
{
// some actions…
}

public void SetCode(Class_A code)
{
// some actions…
}
}
}

// Assembly_C.dll (, Assembly_B)
using Assembly_B;
namespace Assembly_C
{
public class Class_C
{
public void Run()
{
// some actions…
var classB = new Class_B();
classB.SetCode(1);
// some actions…
}
}
}


* This source code was highlighted with Source Code Highlighter .

This is the main thing that I wanted to tell. Of course, during development, we were faced with a lot of nuances, which would be a brute force to describe in one article. But about the most interesting, we will certainly try to write in other articles. In conclusion, I want to say a few words about using Reference Assistant.

As I said earlier, you can download Reference Assistant either from CodePlex or from Visual Studio Gallery . There is a slight difference between them - the extension laid out in the Gallery cannot be used in the Express edition of Visual Studio (this is a limitation of the Visual Studio Gallery), but the extension from CodePlex is possible.

The easiest way to install is to use Extension Manager, a Visual Studio utility.

image

To remove unused assemblies in the context menu of a project or project links (References folder), select “Remove Unused References”.

image

Before deleting unused assemblies, a window will be displayed to confirm the list. You can edit this list if you are sure that the assembly is needed for some reason (for example, it is dynamically connected to the application depending on the settings in the configuration file).

image

You can also turn off the display of the “Unused References List” window using the “Don't show this dialog next time” option. You can enable it again in the extension configuration: Tools -> Options ... -> Reference Assistant menu.

image

More information can be found in the documentation at http://refassistant.codeplex.com in the Downloads section. User's guide describes how to work with the Reference Assistant. It also describes how to enable error logging. You can send us the error description to RefAssistant [at] lardite.com or register it in the error tracker . Developer's guide describes the criteria for evaluating the assembly referenced by the project, and also gives an idea of ​​the overall architecture of the extension.

Source: https://habr.com/ru/post/129027/


All Articles