DEV Community

Cover image for JetBrains ReSharper for Visual Studio
Karen Payne
Karen Payne

Posted on

JetBrains ReSharper for Visual Studio

Introduction

JetBrains ReSharper is an extension for Microsoft Visual Studio that provides numerous features to enhance developer productivity, surpassing that of developers who do not use ReSharper.

The purpose here is to introduce developers to several useful features that many developers just starting out with ReSharper may not be aware of and can immediately benefit from.

The author feels that ReSharper is a must-have addition to all developers' toolbox/

Also, check out 9 ReSharper Features Every .NET Developer Should Know.

Keyboard shortcuts

Once a developer learns which are the most important features for them, yet may not be used often.

Create a GitHub Markdown file, as shown below (see the sample in the source repository), where the last column contains a link to the command documentation.

markdown file sample

Present in Visual Studio

Under Tools menu, External Tools create a new command (requires VS Code to be installed).

  • Title: Resharper keyboard shortcuts
  • Command: c:\users\USERNAME\AppData\Local\Programs\Microsoft VS Code\Code.exe
  • Arguments: The name of the file, for instance resharperShortcuts.md
  • Initial directory: Location of the file under arguments

Writing documentation

With a paid subscription to AI Assistant, a developer never need to worry about having excellent documentation. Yes, Visual Studio can generate help but no where as done with AI Assistant.

Extract Interface

📚 Documentation

This refactoring helps create a new interface based on a selected type. ReSharper suggests choosing members to be transferred to the new interface. After extraction, the original type is updated to implement the new interface.

As shown in the video, each interface is created in its own file, which is in the Models folder. The next step is to create a folder named Interfaces and drag the interfaces into the Interface folder, which will trigger Visual Studio to ask to update each Interfaces namespace.

Adjust Namespaces

📚 Documentation

This command is a bulk fix that helps you synchronize namespaces with folder structure in any scope, which can be as large as your whole solution.

This is a feature I used often. An example is selecting several classes from an instance of Visual Studio and dragging them to another instance of Visual Studio.

Once the above operation is complete, use 'adjust namespaces' to ensure the files match the current project namespaces.

In this example, the files are in a folder called Models.

  • Right-click on the folder.
  • Select Refactor
  • Select Adjust Namespaces.

shows menu

The following dialog appears; click the Next > button for ReSharper to adjust the namespace of each selected file.

the dialog

Convert Static to Extension Method refactoring

📚 Documentation

This refactoring helps you convert a static method to an extension method, provided that the static method:

  • Has at least one parameter.
  • Resides in a non-generic, non-nested static class

This conversion is a time saver, as shown below, here, the shortcut is CTRL + Q, Q

Code templates

📚 Documentation

Code templates that help write common code constructs faster, surround existing code. Below is one of several template types.

Postfix templates

Postfix templates help you transform expressions that you have already typed without jumping backwards.

Example

A developer wants to use a foreach loop on a list with a variable named 'people'.

  • Type people.f
  • Select foreach

A foreach is generated with the cursor on a suggested variable name, which the developer can use or change.

using foreach

PostFix templates

📚 Documentation

Postfix templates help you transform expressions that you have already typed without jumping backwards — just type a dot after an expression and pick a template from the completion list.

For examples and source code see Learn Resharper PostFix and Source Templates

Regular expression to GeneratedRegex attribute

ReSharper automatically detects opportunities to refactor traditional regex usage into the modern [GeneratedRegex] attribute pattern.

Benefits of GeneratedRegex

  • Performance: The regex is compiled and optimized at compile time, leading to faster startup times and lower runtime overhead compared to JIT-compiling the regex at runtime.
  • Efficiency: It avoids the need for manual caching of Regex objects, as the generated code provides a singleton instance.

Example

Given the following code, using a regular expression.

public class Validator
{
    public bool IsEmailValid(string email) 
        => Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$");
}
Enter fullscreen mode Exit fullscreen mode

Place the text cursor in the regular expressions and a light bulb appears with the option Convert to GeneratedRegexAttruibte as shown below.

shows convert dialog

Accept the convert. During the conversion:

  • The class becomes a partial class
  • A second dialog is presented with recommendations for the name of the GeneratedRegex.

After the conversion.

public partial class Validator // Note: The class must be partial
{
    public bool IsEmailValid(string email) 
        => EmailRegex().IsMatch(email);

    // ReSharper generates this attribute and partial method definition
    [GeneratedRegex(@"^[^@\s]+@[^@\s]+\.[^@\s]+$")]
    private static partial Regex EmailRegex();
}
Enter fullscreen mode Exit fullscreen mode

💡 See the above and a more complex code sample in the project ToGeneratedRegexSample.

INotifyPropertyChanged support

📚 Documentation

When a developer wants to use INotifyPropertyChanged, add : INotifyPropertyChanged after the class name, place the text cursor on INotifyPropertyChanged followed by selecting the first option under the red bulb.

shows menu option

Afterwards.

Now INotifyPropertyChanged  has been implemented

Next, place the text cursor on a property, select the yellow 💡 which ReSharper provides several options to implement property change.

Click a top-level menu item to implement for the current property while clicking a sub menu option while implement property change for all class properties.

shows menu options

In this case the selected option was To Property with 'SetField'.

shows each property using SetField

Using AI Assistant

With the class open, try the following prompt:

Add INotifyPropertyChanged to Person class using SetField

Once satisfied, use the copy button in the top right corner for the response, followed by overwriting the current class.

Move Types into Matching Files

📚 Documentation

This feature is useful, for instance, when a developer creates several classes in a single file with the intention of later extracting each class to its own file.

The reason for creating multiple classes in one file is to avoid having to switch between each class in Visual Studio. The author does this because she can also create the classes faster than manually creating each class file. When finished, ReSharper will move each class to a new file.

Example

Given the following with several classes in a single file, Person.cs.

public class Person
{
    public int Id { get; set; }
    public bool Active { get; set; }
    public required string FirstName { get; set; }
    public required string LastName { get; set; }
    public required Gender Gender { get; set; }
    public required Address Address { get; set; }
}
public class Address
{
    public int Id { get; set; }
    public required string Street { get; set; }
    public required string City { get; set; }
    public required string State { get; set; }
}
public enum Gender
{
    Male,
    Female,
    Other
}
Enter fullscreen mode Exit fullscreen mode
  • Right-click on the file in Solution Explorer
  • Select Refactor
  • Select Move Type to Matching Files...

shows menu item Move Type to Matching Files...

A dialog is presented with options. Click Next button to complete the move each class to new files.

options dialog

💡 Try it in project MoveTypesIntoMatchingFiles\Models\Person.cs

Shows before and after

Convert Anonymous to Named Type refactoring

📚 Documentation

Anonymous types are very convenient when you need to process a result of a query locally. However, if you need to pass the result around your program or if there are several queries that return the similar name/value parts, you may probably need a named type.

Example

Source code

In the following method, the variable duplicates can only be used within the method.

public static void GetDuplicateActiveMembers(IEnumerable<Member> list)
{
    var duplicates = list
        .GroupBy(member => new { member.FirstName, member.SurName, member.Active })
        .Where(group => group.Key.Active && group.Count() > 1)
        .Select(group => new
        {
            FullName = $"{group.Key.FirstName} {group.Key.SurName}",
            Members = group.ToList()
        })
        .ToList();
}
Enter fullscreen mode Exit fullscreen mode

To allow duplicates to be returned, place the cursor on new and press the shortcut key assigned to ReSharper_Anonymous2Declared command.

shows keyboard shortcut

The following dialog appears; select a name, scope, and other options as needed. Click the Next button and ReSharper generates code.

dialog

Modify the return type as shown below.

    public static List<GroupMember> GetDuplicateActiveMembers(IEnumerable<Member> list) =>
        list
            .GroupBy(member => (Name: member.FirstName, member.SurName, member.Active))
            .Where(group =>
            {
                if (!group.Key.Active) return false;
                return group.Count() > 1;

            })
            .Select(item =>
                new GroupMember($"{item.Key.Name} {item.Key.SurName}", [.. item]))
            .ToList();
Enter fullscreen mode Exit fullscreen mode

The convert to name type can also be accessed from ReSharper menu.

menu option

Import missing namespaces

📚 Documentation

When you use types whose namespaces have not been imported in the file, ReSharper helps you locate these types and add the missing namespace import directives.

Example 1

A developer obtains the following code. Without ReSharper, Visual Studio will ask for missing using statements and provide recommendations.

Note
The reason for using a large example is for showing how well auto Import missing namespaces works.

public class Info
{
    public readonly record struct CallerDetails(string? AssemblyName, string? AssemblyVersion,
        string? TargetFramework, string? TypeName, string? MethodName, string? FilePath, int LineNumber);

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static CallerDetails BuildCallerDetails(string? memberName, string? filePath, int lineNumber)
    {
        var callingAsm = Assembly.GetCallingAssembly();
        string? typeName = null;

        try
        {
            var st = new StackTrace(skipFrames: 1, fNeedFileInfo: false);
            var frame = st.GetFrame(0);
            typeName = frame?.GetMethod()?.DeclaringType?.FullName;
        }
        catch
        {
            // Best-effort; leave typeName null if anything goes sideways.
        }

        var asmName = callingAsm.GetName();
        var framework = callingAsm.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;

        return new CallerDetails(
            AssemblyName: asmName?.Name,
            AssemblyVersion: asmName?.Version?.ToString(),
            TargetFramework: framework,
            TypeName: typeName,
            MethodName: memberName,
            FilePath: filePath,
            LineNumber: lineNumber);
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static string GetCopyright()
    {
        var asm = Assembly.GetCallingAssembly();
        var attr = asm.GetCustomAttribute<AssemblyCopyrightAttribute>();
        return attr?.Copyright ?? "No copyright information found.";
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static string GetCopyright(out CallerDetails caller, [CallerMemberName] string? memberName = null, [CallerFilePath] string? filePath = null, [CallerLineNumber] int lineNumber = 0)
    {
        caller = BuildCallerDetails(memberName, filePath, lineNumber);
        return GetCopyright();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static string GetCompany()
    {
        var asm = Assembly.GetCallingAssembly();
        var attr = asm.GetCustomAttribute<AssemblyCompanyAttribute>();
        return attr?.Company ?? "No company information found.";
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static string GetCompany(out CallerDetails caller, [CallerMemberName] string? memberName = null, [CallerFilePath] string? filePath = null, [CallerLineNumber] int lineNumber = 0)
    {
        caller = BuildCallerDetails(memberName, filePath, lineNumber);
        return GetCompany();
    }
    [MethodImpl(MethodImplOptions.NoInlining)]
    public static string GetProduct()
    {
        var asm = Assembly.GetCallingAssembly();
        var attr = asm.GetCustomAttribute<AssemblyProductAttribute>();
        return attr?.Product ?? "No product information found.";
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static string GetProduct(out CallerDetails caller, [CallerMemberName] string? memberName = null, [CallerFilePath] string? filePath = null, [CallerLineNumber] int lineNumber = 0)
    {
        caller = BuildCallerDetails(memberName, filePath, lineNumber);
        return GetProduct();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static Version GetVersion()
    {
        var asm = Assembly.GetCallingAssembly();
        return asm.GetName().Version ?? new Version(1, 0, 0, 0);
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static Version GetVersion(out CallerDetails caller, [CallerMemberName] string? memberName = null, [CallerFilePath] string? filePath = null, [CallerLineNumber] int lineNumber = 0)
    {
        caller = BuildCallerDetails(memberName, filePath, lineNumber);
        return GetVersion();
    }
}
Enter fullscreen mode Exit fullscreen mode

With ReSharper, after pasting the code into, in this case, Info.cs, the following statements are added, which allow the code to compile.

using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
Enter fullscreen mode Exit fullscreen mode

Example 2

In some cases, auto import is not available, and the developer will be prompted as shown below.

prompting for recommendation for using statement


Summary

ReSharper has more features than the average developer will ever need, while this is true. The best way to determine what is best for you is to take the time to read the great documentation and create a markdown file as described at the start of this article.

There are three subscriptions

  • ReSharper which will fit every developer's needs
  • dotUltimate which besides ReSharper has several helpful tools and JetBrains AI Pro
  • All Products Pack which besides ReSharper has 18 tools.

How to decide? When unsure, select ReSharper or start a 30-day trial. Download a product and follow the instructions provided.

Author comment

The author has been using ReSharper since before 2015.

There are times when she has three instances of Microsoft Visual Studio open at once and ReSharper never hinders performance.


Source code

Top comments (0)