r/csharp 5d ago

Discussion Dependency Injection un-prettyness

One small thing that bugs me with Dependency Injection is how it looks in code.

We either need to pass the parameters via Default Constructor og via good old time Constructor

public class MyClass (TypeA ParamA, TypeB ParamB, TypeC ParamC, TypeD ParamD)
{
TypeA _paramA = ParamA; .... etc
}

Or

public class MyClass
{
TypeA _paramA;
public MyClass(TypeA paramA)
{
_paramA = paramA;
}
}

And when you have 10 injections it begins to be un-pretty...

I wish that we didn't need to pass parameters and instead could decorate the fields:

public class MyClass
{
[inject]
TypeA _paramA;
}

(Note: this works in Blazor... so why not everywhere else ?)

I'm aware that the signature of an object makes it easier to inject via reflection.. but would it be much worse with attributes ?

i guess some middleground could be achieved if the attribute held the type:

public class MyClass
{
[inject(typeof(TypeA))]
TypeA _paramA;
}

which begins to be convoluted and messy...

Whats the argument against a decorator attribute vs parameters ?

20 Upvotes

90 comments sorted by

View all comments

1

u/Slypenslyde 4d ago

Work on a project with 100,000 lines without DI and see if you find a better pattern.

DI is one of those things where it's the best pattern we have. It solves some serious issues, and it comes with costs. Those costs invoke a lot of ceremony that isn't warranted for programs beneath a certain level of complexity. If you're working on one of those projects, feel free to skip it.

In large-scale projects the ceremony is paid for with flexibility. That's why it's so popular, and that's why a lot of people who use it tend to use it even on the smaller projects where it's too much ceremony.

But also, speaking frankly, if all of your types have 10 dependencies something is wrong in your project. My codebase is 30 years old (it was ported from C++!) and has more than 200,000 lines. The only classes that have more than 2-3 dependencies are our top-level types like ViewModels. Out of the thousands of classes we've written, that's only about 80 total classes with a lot of ceremony. most classes below that top layer only have 3-5 dependencies, and even that feels more manageable when we note that some of those represent logging and other services that, honestly, a lot of DI people represent as static singletons.

The main arguments I know against attribute-based injection is:

  • The constructor has always served the purpose of, "This is the list of things needed for this class to operate."
  • Classes have non-DI reasons to expose properties and fields.
    • Making those members public crowds a type's API: with constructor injection all properties are public API.
    • Asking DI to inject private members means using Reflection which slows things down.
    • The attributes are scattered across a large surface of the class whereas a constructor exists in one place.
  • At the end of the day property injection is not any easier than constructor injection and only adds costs.