Skip to content

Func <T> – Dynamic Instantiation

Last updated on March 28, 2019

Think of a scenario where we want more than one instance of a given service, or the decision, whether to instantiate a component is made at run time. For such scenarios injecting a service as a Direct or Lazy dependency will not be enough. However, injecting the service dependency as a Func do.

 

Using Func <T>

Consider the following example, where we have a PaymentProcessor class. The PaymentProcessor class has dependency on the IPaymentGateway service. And, the decision whether to instantiate the payment gateway or not, is based on the user’s choice of PaymentMode (say cash or card). The below code shows the use of a Func in this scenario:

public interface IPaymentGateway
{
    void InitiatePayment(TransactionDetails transactionDetails);
}

public class PaymentProcessor
{
    private Func<IPaymentGateway> _paymentGatewayInitializer;
    public PaymentProcessor(Func<IPaymentGateway> paymentGatewayInitializer)
    { 
        _paymentGatewayInitializer = paymentGatewayInitializer; 
    }

    public void ProcessPayment(TransactionDetails transactionDetails)
    {
        if(transactionDetails.PaymentMode != PaymentMode.Cash)
        {
            using(var paymentGateway = _paymentGatewayInitializer())
            {
                paymentGateway.InitiatePayment(amount);
            }
        }
    }
}

 

It is important to note that there can be a scenario where a user chooses to pay in cash. In this case, we don’t need to instantiate IPaymentGateway. Also, we shall have a new instance of  IPaymentGateway, every time we initiate a new payment transaction.

 

Register & Resolve

The registration of the components will be quite simple and will be as shown in below code:

var builder = new ContainerBuilder();
builder.RegisterType<TransactionDetails>().InstancePerLifetimeScope();
builder.RegisterType<PaymentProcessor>().InstancePerLifetimeScope();
builder.RegisterType<IPaymentGateway>().InstancePerLifetimeScope();

var container = builder.Build();
using(var scope = container.BeginLifetimeScope())
{
    // Initialize transactionDetails object here and pass
    // it to the PaymentProcessor.
    var paymentProcessor = scope.Resolve<PaymentProcessor>();
    paymentProcessor.ProcessPayment(transactionDetails);
}

 

Because the IPaymentGateway has been registered as a Func, the container will not resolve it with  the PaymentProcessor. In fact, the container leaves it up to the PaymentProcessor to initialize IPaymentGateway using the auto-generated factory method. Therefore, as we can see in the above code, we are initializing the IPaymentGateway using the _paymentGatewayInitializer.

 

Lifetime Scope

In the above example, we have registered the IPaymentGateway as InstancePerLifetimeScope(). This is to say that, every time we call the Func<IPaymentGateway> we will get a new instance of IPaymentGateway. However, if we register a component as SingleInstance() and call Func<T> multiple times we will get the same object instance every time.

Published inDesign Patterns & Principles