0

I'm using Rider on a .NET project that has many classes that inject a large number of services and take up a lot of real estate at the top of the file like this:

public class MyCoolService: IMyCoolService
{
    private readonly IMyInjectedService1: myInjectedService1;
    private readonly IMyInjectedService2: myInjectedService2;
    private readonly IMyInjectedService3: myInjectedService3;
    private readonly IMyInjectedService4: myInjectedService4;
    private readonly IMyInjectedService5: myInjectedService5;
    private readonly IMyInjectedService6: myInjectedService6;
    private readonly IMyInjectedService7: myInjectedService7;
    private readonly IMyInjectedService8: myInjectedService8;
    private readonly IMyInjectedService9: myInjectedService9;
    private readonly IMyInjectedService10: myInjectedService10;
    private readonly IMyInjectedService11: myInjectedService11;
    private readonly IMyInjectedService12: myInjectedService12;
    private readonly IMyInjectedService13: myInjectedService13;
    private readonly IMyInjectedService14: myInjectedService14;
    private readonly IMyInjectedService15: myInjectedService15;
    private readonly IMyInjectedService16: myInjectedService16;
    private readonly IMyInjectedService17: myInjectedService17;
    private readonly IMyInjectedService18: myInjectedService18;
    private readonly IMyInjectedService19: myInjectedService19;
    private readonly IMyInjectedService20: myInjectedService20;

    public MyCoolService(IMyInjectedService1 myInjectedService1,
        IMyInjectedService2 myInjectedService2, IMyInjectedService3 myInjectedService3,
        IMyInjectedService4 myInjectedService4, IMyInjectedService5 myInjectedService5,
        IMyInjectedService6 myInjectedService6, IMyInjectedService7 myInjectedService7,
        IMyInjectedService8 myInjectedService8, IMyInjectedService9 myInjectedService9,
        IMyInjectedService10 myInjectedService10, IMyInjectedService11 myInjectedService11,
        IMyInjectedService12 myInjectedService12, IMyInjectedService13 myInjectedService13,
        IMyInjectedService14 myInjectedService14, IMyInjectedService15 myInjectedService15,
        IMyInjectedService16 myInjectedService16, IMyInjectedService17 myInjectedService17,
        IMyInjectedService18 myInjectedService18, IMyInjectedService19 myInjectedService19,
        IMyInjectedService20 myInjectedService20)
    {
        this.myInjectedService1 = myInjectedService1;
        this.myInjectedService2 = myInjectedService2;
        this.myInjectedService3 = myInjectedService3;
        this.myInjectedService4 = myInjectedService4;
        this.myInjectedService5 = myInjectedService5;
        this.myInjectedService6 = myInjectedService6;
        this.myInjectedService7 = myInjectedService7;
        this.myInjectedService8 = myInjectedService8;
        this.myInjectedService9 = myInjectedService9;
        this.myInjectedService10 = myInjectedService10;
        this.myInjectedService11 = myInjectedService11;
        this.myInjectedService12 = myInjectedService12;
        this.myInjectedService13 = myInjectedService13;
        this.myInjectedService14 = myInjectedService14;
        this.myInjectedService15 = myInjectedService15;
        this.myInjectedService16 = myInjectedService16;
        this.myInjectedService17 = myInjectedService17;
        this.myInjectedService18 = myInjectedService18;
        this.myInjectedService19 = myInjectedService19;
        this.myInjectedService20 = myInjectedService20;
    }
}

As I navigate around the code I like to use code folding to get an overview of a class before unfolding and diving into the particular section I want to work on. At the moment Rider can automatically fold the constructor body which hides some detail:

public class MyCoolService: IMyCoolService
{
    private readonly IMyInjectedService1: myInjectedService1;
    private readonly IMyInjectedService2: myInjectedService2;
    private readonly IMyInjectedService3: myInjectedService3;
    private readonly IMyInjectedService4: myInjectedService4;
    private readonly IMyInjectedService5: myInjectedService5;
    private readonly IMyInjectedService6: myInjectedService6;
    private readonly IMyInjectedService7: myInjectedService7;
    private readonly IMyInjectedService8: myInjectedService8;
    private readonly IMyInjectedService9: myInjectedService9;
    private readonly IMyInjectedService10: myInjectedService10;
    private readonly IMyInjectedService11: myInjectedService11;
    private readonly IMyInjectedService12: myInjectedService12;
    private readonly IMyInjectedService13: myInjectedService13;
    private readonly IMyInjectedService14: myInjectedService14;
    private readonly IMyInjectedService15: myInjectedService15;
    private readonly IMyInjectedService16: myInjectedService16;
    private readonly IMyInjectedService17: myInjectedService17;
    private readonly IMyInjectedService18: myInjectedService18;
    private readonly IMyInjectedService19: myInjectedService19;
    private readonly IMyInjectedService20: myInjectedService20;

    public MyCoolService(IMyInjectedService1 myInjectedService1,
        IMyInjectedService2 myInjectedService2, IMyInjectedService3 myInjectedService3,
        IMyInjectedService4 myInjectedService4, IMyInjectedService5 myInjectedService5,
        IMyInjectedService6 myInjectedService6, IMyInjectedService7 myInjectedService7,
        IMyInjectedService8 myInjectedService8, IMyInjectedService9 myInjectedService9,
        IMyInjectedService10 myInjectedService10, IMyInjectedService11 myInjectedService11,
        IMyInjectedService12 myInjectedService12, IMyInjectedService13 myInjectedService13,
        IMyInjectedService14 myInjectedService14, IMyInjectedService15 myInjectedService15,
        IMyInjectedService16 myInjectedService16, IMyInjectedService17 myInjectedService17,
        IMyInjectedService18 myInjectedService18, IMyInjectedService19 myInjectedService19,
        IMyInjectedService20 myInjectedService20)
    {...}
}

However this leaves the private fields and constructor parameters. What I end up doing is using the Ctrl+. custom code folding to also hide these areas (then I can unfold them if needed). My issue is this is a manual process every time. Is there a way to get Rider to recognise these areas as foldable and do it automatically?

public class MyCoolService: IMyCoolService
{
    ...

    public MyCoolService(...)
    {...}
}
pjpscriv
  • 866
  • 11
  • 20
  • 1
    If you need to inject 20 services into one class that's a good indication you need to refactor that class into multiple classes as it's likely trying to do too much. That or all those services are oversimplified and could be merged together. – juharr May 04 '21 at 02:22
  • Agreed. Although it's a large legacy codebase and a refactor like that would be quite substantial change. At the moment I'm just looking for ways to make a it a bit nicer to work with – pjpscriv May 07 '21 at 05:38
  • I found that [Rider lets you define custom foldable regions](https://blog.jetbrains.com/idea/2012/03/custom-code-folding-regions-in-intellij-idea-111/) with `//` comments. This isn't an ideal solution - I would prefer to have the IDE recognise the regions automatically - but it is a solution – pjpscriv May 07 '21 at 05:55
  • Fun fact this question prompted some [discussion on twitter](https://twitter.com/RogerAlsing/status/1409427383002841088) – pjpscriv Jun 04 '22 at 03:44

2 Answers2

1

Ideally you would not have that many services injected into a single class. The ideal approach would be to refactor the code, to not have so many dependencies.

But, it is understandable that this may not be an straightforward option. One thing you can do is wrap the code, using a #region statement. This tells the IDE to fold the code in that specified region.

Below is an example of how you can use this in your example

public class MyCoolService: IMyCoolService
{
    #region Injected properties

    private readonly IMyInjectedService1: myInjectedService1;
    private readonly IMyInjectedService2: myInjectedService2;
    private readonly IMyInjectedService3: myInjectedService3;
    private readonly IMyInjectedService4: myInjectedService4;
    private readonly IMyInjectedService5: myInjectedService5;
    private readonly IMyInjectedService6: myInjectedService6;
    private readonly IMyInjectedService7: myInjectedService7;
    private readonly IMyInjectedService8: myInjectedService8;
    private readonly IMyInjectedService9: myInjectedService9;
    private readonly IMyInjectedService10: myInjectedService10;
    private readonly IMyInjectedService11: myInjectedService11;
    private readonly IMyInjectedService12: myInjectedService12;
    private readonly IMyInjectedService13: myInjectedService13;
    private readonly IMyInjectedService14: myInjectedService14;
    private readonly IMyInjectedService15: myInjectedService15;
    private readonly IMyInjectedService16: myInjectedService16;
    private readonly IMyInjectedService17: myInjectedService17;
    private readonly IMyInjectedService18: myInjectedService18;
    private readonly IMyInjectedService19: myInjectedService19;
    private readonly IMyInjectedService20: myInjectedService20;

    #endregion

    public MyCoolService(
        IMyInjectedService1 myInjectedService1,
        IMyInjectedService2 myInjectedService2, IMyInjectedService3 myInjectedService3,
        IMyInjectedService4 myInjectedService4, IMyInjectedService5 myInjectedService5,
        IMyInjectedService6 myInjectedService6, IMyInjectedService7 myInjectedService7,
        IMyInjectedService8 myInjectedService8, IMyInjectedService9 myInjectedService9,
        IMyInjectedService10 myInjectedService10, IMyInjectedService11 myInjectedService11,
        IMyInjectedService12 myInjectedService12, IMyInjectedService13 myInjectedService13,
        IMyInjectedService14 myInjectedService14, IMyInjectedService15 myInjectedService15,
        IMyInjectedService16 myInjectedService16, IMyInjectedService17 myInjectedService17,
        IMyInjectedService18 myInjectedService18, IMyInjectedService19 myInjectedService19,
        IMyInjectedService20 myInjectedService20)
    {

        #region property assignments

        this.myInjectedService1 = myInjectedService1;
        this.myInjectedService2 = myInjectedService2;
        this.myInjectedService3 = myInjectedService3;
        this.myInjectedService4 = myInjectedService4;
        this.myInjectedService5 = myInjectedService5;
        this.myInjectedService6 = myInjectedService6;
        this.myInjectedService7 = myInjectedService7;
        this.myInjectedService8 = myInjectedService8;
        this.myInjectedService9 = myInjectedService9;
        this.myInjectedService10 = myInjectedService10;
        this.myInjectedService11 = myInjectedService11;
        this.myInjectedService12 = myInjectedService12;
        this.myInjectedService13 = myInjectedService13;
        this.myInjectedService14 = myInjectedService14;
        this.myInjectedService15 = myInjectedService15;
        this.myInjectedService16 = myInjectedService16;
        this.myInjectedService17 = myInjectedService17;
        this.myInjectedService18 = myInjectedService18;
        this.myInjectedService19 = myInjectedService19;
        this.myInjectedService20 = myInjectedService20;

        #endregion
    }
}

The IDE will be able to fold the constructor. This is how your code would look like after it's folded

enter image description here

Tejas Parnerkar
  • 201
  • 1
  • 5
  • It looks like you're using VSCode but this answer works in Rider too. The only difference is I put a region around the parameters and didn't need to put one around the property assignments as Rider recognises the constructor body as a foldable area – pjpscriv Jul 07 '21 at 09:04
1

While I agree with @tejas-parnerkar's answer, I want to offer another view. If you're having to inject that many classes, it means the class is trying to do too much, i.e. its responsibilities are probably not bounded correctly.

If your class has these methods:

class ChoreRunner
{
    private VacuumCleaner _vacuumCleaner;
    private Sink _sink;
    private WashingMachine _washingMachine;
    private Kitchen _kitchen;
    // ...other dependencies
    
    public Task SweepFloor() {}
    public Task DoDishes() {}
    public Task FeedKids() {}
    public Task DoLaundry() {}
    // ...
}

The real solution is to break up that "god" class into smaller classes that have tightly constrained responsibilities.

interface IChore
{
    Task Do(); 
}

class SweepFloor: IChore
{
    private VacuumCleaner _vacuum;
    public Task Do() {/* ... */}
}

class WashDishes: IChore
{
    private Sink _sink;
    private IDirtyDishSource _dirtyDishSource;

    // ...
    public Task Do() {/* ... */}
}

class Laundry: IChore
{
    private WashingMachine _washingMachine;
    private Dryer _dryer;
    // ...
    public Task Do() {/* ... */}
}

class FeedKids : IChore
{
    private Kitchen _kitchen;
    private IKidProvider _kidProvider;
    // ...
    public Task Do() {/* ... */}
}
abdusco
  • 9,700
  • 2
  • 27
  • 44
  • As I said to @juharr's comment a refactor like that isn't very practical at the moment. I'm just looking to navigate the code a bit easier to start with :) – pjpscriv Jul 07 '21 at 09:08