1

I am having a hard time wrapping my head around this function with multiple fat arrows.

services.AddTransient<ServiceA>();
services.AddTransient<ServiceB>();
services.AddTransient<ServiceC>();

services.AddTransient<ServiceResolver>(serviceProvider => key =>
{
    switch (key)
    {
        case "A":
            return serviceProvider.GetService<ServiceA>();
        case "B":
            return serviceProvider.GetService<ServiceB>();
        case "C":
            return serviceProvider.GetService<ServiceC>();
        default:
            throw new KeyNotFoundException(); // or maybe return null, up to you
    }
});

In C# i have never come across this function style serviceProvider => key =>. Can anyone please explain to me what it means

jhon.smith
  • 1,963
  • 6
  • 30
  • 56

2 Answers2

2

This is an lambda expression returning another lambda expression.

Not sure what is ServiceResolver but I would say that it is a delegate looking something like this:

// IService - shared contract for ServiceA, ServiceB, ...
public delegate IService ServiceResolver(string key); 

Then the invocation services.AddTransient<ServiceResolver>(serviceProvider => ...) is a call to AddTransient method accepting a Func<T, TResult> delegate, which is constructed via lambda expression and returning another delegate constructed also via lambda expression (key => {...}).

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
0

Yes indeed, it is a high-order function, so it's a function which returns another function.

Let's see the last AddTransient method call's parameter from inside out:

key =>
{
    switch (key)
    {
        case "A":
            return serviceProvider.GetService<ServiceA>();
        case "B":
            return serviceProvider.GetService<ServiceB>();
        case "C":
            return serviceProvider.GetService<ServiceC>();
        default:
            throw new KeyNotFoundException(); // or maybe return null, up to you
    }
};

You can describe this function as a Func<String, Object> if ServiceA ... ServiceC don't have any common ancestor / does not implement a common interface.

So you have a function which receives a string (key) and returns an object.


serviceProvider => key =>
{
    ...
}

This function can be described with Func<ServiceProvider, Func<String, Object>>. If this function receives a serviceProvider instance during its method call then it will return a function which anticipates a string and returns an object.

If you have a function like the above then you can call the inner via the outer:

Func<ServiceProvider, Func<String, Object>> factoryMethodRegister = ...
Func<String, Object> factoryMethod = factoryMethodRegister(sp);
Object instance = factoryMethod(key);

//OR
Func<ServiceProvider, Func<String, Object>> factoryMethodRegister = ...
Object instance = factoryMethodRegister(sp)(key);
Peter Csala
  • 17,736
  • 16
  • 35
  • 75