Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency injection and consumption in C# classes

Say you have

public class Garage {
    public bool AddCar(Car car) { //omitted for brevity 
    }
}

you add it as services.AddScoped<Garage>();

now you want to inject it into Car class:

public class Car(Garage g1) {
    private readonly Garage g1= g1;

    public bool ModifyCarAndStoreInGarage(Car car)
        // modify car properties
        g1.AddCar(car);
    }
}

I am curious to know how does ModifyCarAndStoreInGarage know which instance of g1 is being referenced, the one from private readonly or one being injected?

Is there a sequence to events?

like image 673
Aeseir Avatar asked Dec 03 '25 21:12

Aeseir


2 Answers

You're right to think about scope and resolution, but there's no confusion here: the g1 inside ModifyCarAndStoreInGarage refers to the private readonly field, which was initialized via constructor injection.

When you write private readonly Garage g1 = g1; in the constructor, you're assigning the injected instance (parameter g1) to the private field g1. After that, anywhere in the class, including in ModifyCarAndStoreInGarage, g1 refers to that private field — which holds the same instance that was injected by the DI container.

So there's no duplication or ambiguity — just a constructor parameter and a class member with the same name (which is a bit confusing but legal and common in C#).

like image 161
Nico Volling Avatar answered Dec 05 '25 10:12

Nico Volling


I am curious to know how does ModifyCarAndStoreInGarage know which instance of g1 is being referenced, the one from private readonly or one being injected?

TL;DR

g1 in the g1.AddCar(car); is the g1 field you have declared in your class private readonly Garage g1. It stores the value from the constructor parameter g1 (the one from Car(Garage g1)), so the used value would be the same.

Details

The syntax you used is relatively new one (introduced in C# 12) and is called Primary Constructors.

In this particular case you can see the shadowing of primary constructor parameter with the field you have declared. Basically your code is analogous to the following:

public class Car
{
    private readonly Garage g1;

    public Car(Garage g1)
    {
        this.g1 = g1;
        base..ctor();
    }

    public void ModifyCarAndStoreInGarage(Car car)
    {
        g1.AddCar(car);
    }
}

Decompilation @sharplab.io

If you are really-really interested in how this does work you can check out the Lookup section of the Primary Constructors feature spec:

In other words, primary constructor parameters are in scope throughout the declaring type body. They shadow members of the declaring type within an initializer of a field, property or event of the declaring type, or within the argument_list of class_base of the declaring type. They are shadowed by members of the declaring type everywhere else.

Thus, in the following declaration:

class C(int i)
{
    protected int i = i; // references parameter
    public int I => i; // references field
}

The initializer for the field i references the parameter i, whereas the body of the property I references the field i.

The only profit from such shadowing is that declaring the member with the same name you can make it readonly. I.e. without shadowing g1 is actually mutable:

public class Car(Garage g1) {
    public void  ModifyCarAndStoreInGarage(Car car) {
        g1 = new Garage(); // you can overwrite the value completely
    }
}
like image 38
Guru Stron Avatar answered Dec 05 '25 09:12

Guru Stron



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!