Home > database >  How to initialize services in a chain constructor aproach
How to initialize services in a chain constructor aproach

Time:11-12

I have the following Main class that takes a dataStore as a constructor parameter for Foo class.

In the Main class I want to pass to Foo only one constructor, 'dataStore', but in the Foo constructor class I want to initialize besides dataStore also a couple of other services.

public class Main
{
    private DataStore dataStore = new DataStore();

    public Main()
    {
        var foo = new Foo(dataStore);
        foo.DoSomething();
    }
}

I've try this approach with chaining the Foo constructors, but only the dataStore is initialized, _serviceOne and _serviceTwo are null in this case (because I'm passing null in the construnctor chaining, and the second construnctor is never called).

public class Foo
{
    private readonly DataStore _dataStore;
    private readonly IServiceOne _serviceOne;
    private readonly IServiceTwo _serviceTwo;


    public Foo(DataStore dataStore) : this(null, null)
    {
        _dataStore = dataStore;
    }

    public Foo(IServiceOne serviceOne, IServiceTwo serviceTwo)
    {
        _serviceOne = serviceOne;
        _serviceTwo = serviceTwo;
    }

    public void DoSomething()
    {
        // do something
    }
}

If I try to pass all the data to only one Foo constructor, I need to pass _dataStore , _serviceOne and _serviceTwo as arguments for Foo as well in the Main classs and I don't what this.

    public Foo(DataStore dataStore, IServiceOne serviceOne, IServiceTwo serviceTwo)
    {
        _dataStore = dataStore;
        _serviceOne = serviceOne;
        _serviceTwo = serviceTwo;
    }

Is there a way to pass only dataStore as argument in Main class, and also to initialize all the fields(_dataStore,_serviceOne,_serviceTwo) in Foo constructor class?

Esentially I what to pass to Foo only the _dataStore and to instantiate the rest of the services in the Foo class itself:

var foo = new Foo(dataStore);

CodePudding user response:

I'm not sure why you need to do this. You can just initialize the IServiceOne, IServiceTwo references in the Foo(DataStore) constructor. But if you really need to, it definitely works, here is a working dotnetfiddle adapted from your code:

https://dotnetfiddle.net/2FDjix

using System;

public class DataStore {}

public interface IServiceOne {}
public interface IServiceTwo {}

public class ServiceOne : IServiceOne {}
public class ServiceTwo : IServiceTwo {}

public class Program
{
        private static DataStore dataStore = new DataStore();
 
        public static void Main()
        {
            var foo = new Foo(dataStore);
            foo.DoSomething();
       }
}

public class Foo
{
    private readonly DataStore _dataStore;
    private readonly IServiceOne _serviceOne;
    private readonly IServiceTwo _serviceTwo;


    public Foo(DataStore dataStore) : this(null, null)
    {
        Console.WriteLine("Foo 1st construcor");
        _dataStore = dataStore;
    }

    public Foo(IServiceOne serviceOne, IServiceTwo serviceTwo)
    {
        Console.WriteLine("Foo 2nd constructor");
        _serviceOne = serviceOne;
        _serviceTwo = serviceTwo;
    }

    public void DoSomething()
    {
        // do something
    }
}

Output:

Foo 2nd constructor

Foo 1st construcor

CodePudding user response:

If the intent is

  • You'll always pass a DataStore to the constructor and the class will use that.
  • You may or may not want to pass IServiceOne or IServiceTwo. If you don't pass them, them class will provide its own implementation

...there are a few ways to do that. Here's one.

public Foo(DataStore dataStore) : this(dataStore, null, null)
{
}

public Foo(DataStore dataStore, IServiceOne serviceOne, IServiceTwo serviceTwo)
{
    _dataStore = dataStore;
    _serviceOne = serviceOne ?? new SomeImplementationOne();
    _serviceTwo = serviceTwo ?? new SomeImplementationTwo();
}

You might wonder - why even have the first constructor at all if you can just call the second one and pass nulls for serviceOne and serviceTwo?

The answer is that you could do that. Having two constructors just makes it a little bit clearer that there are choices. If all you had was the second constructor, someone might not know that they had the option of passing null and letting the class provide its own implementation.

Even then, it's a little confusing because it might not be obvious to the caller that if they pass null, the class will replace the null with something else.

Here's another option:

public Foo(DataStore dataStore)
{
    _dataStore = dataStore ?? throw new ArgumentNullException(nameof(dataStore));
    _serviceOne = new SomeImplementationOne();
    _serviceTwo = new SomeImplementationTwo();
}

public Foo(DataStore dataStore, IServiceOne serviceOne, IServiceTwo serviceTwo)
{
    _dataStore = dataStore ?? throw new ArgumentNullException(nameof(dataStore));
    _serviceOne = serviceOne ?? throw new ArgumentNullException(nameof(serviceOne));
    _serviceTwo = serviceTwo ?? throw new ArgumentNullException(nameof(serviceTwo));
}

This keeps the constructors separate. One uses the dataStore argument and supplies the other two. The second constructor requires that you pass all three.

It might not be worth overthinking. It's good to imagine someone else using the class later and thinking about which will make it the most obvious.

  • Related