C# – Is it possible to method-inject an object call

cdelegatesnet

Consider this example of method injection:

static void SaveEmail(Func<string> getEmailFunction)
{
    dbcontext.SaveEmail(getEmailFunction());
}

static string GetEmail()
{
    var frmUser = GetUserForm();
    return frmUser.GetEmail();
}

static void ExampleCall()
{
    SaveEmail(GetEmail);
}

In this example, we are injecting a Func<string> that returns the email address of the user. The SetEmail method uses this as a sort of callback to obtain the user's address. This way we avoid SetEmail having a hardcoded dependency on the code that obtains the user's form and reads its fields.

Is it possible to do this without the static version of GetEmail? Something like this:

static void SaveEmail(Func<string> getEmailFunction)
{
    dbcontext.SaveEmail(getEmailFunction());
}

static void ExampleCall()
{
    var frmUser = GetUserForm();
    SaveEmail(frmUser.GetEmail);
}

Best Answer

Sure you can but why? This isn't a question of why you would ever do that but why you should do that as opposed to injecting a class that does it for you via interface (dependency injection). You could pass in the class to the method call or to the constructor of the class that has the save email (IOC containers prefer the second approach).

So in the case that you presented, this will work just fine if you have short methods that do not go crazy, because then you may as well have a class do that for you. In fact, there is nothing against having a class that only has like 10 lines of code in it. The new "microservice" architecture ideas could have 10 lines of code in an entire program.

So you could have an interface that looks something like this:

public interface IEmailProvider
{
    string GetEmail();
}

And a class that does something easy:

public class SettingsEmailProvider : IEmailProvider
{
    public string GetEmail()
    {
        // sudo code read from app settings
        return ReadFromSettingFile("useremail");
    }
}

But now you need to test that code so you create a moq class

public class MoqEmailProvider : IEmailProvider
{
    public string GetEmail()
    {
        return "foo@bar.com";
    }
}

Ok so far we could have just done this in a function call passed in, but lets say you need something more complicated in the future, like go into a Facebook API to get the user's email address from there.

public class FacebookEmailProvider : IEmailProvider
{
    private readonly IFacebookCredentialProvider _creds;
    private readonly IFacebookClient _client;
    private readonly IEmailProvider _fallbackProvider;

    public ILog Logger {get;set;}

    public FacebookEmailProvider(IFacebookCredentialProvider creds, IFacebookClient client, IEmailProvider fallbackProvider)
    {
        _creds = creds;
        _client = client;
        _fallbackProvider = fallbackProvider;
    }

    public string GetEmail()
    {
        //Try to get the email from facebook
        string email;
        try
        {
            var credentials = _creds.GetCredentials();
            var task = Task.Factory.StartNew(()=>{
                _client.Login(credentials);
                email = _client.GetUserEmail();
                }
            task.wait();
            if(string.IsNullOrEmpty(email))
                email = _fallbackProvider.GetEmail();
        }
        catch(Exception ex)
        {
            Logger.Error(ex);
            email = _fallbackProvider.GetEmail();
        }
    }
}

So now we have gone a bit more complicated, and then your calling class will need to provide all of this if it was just a Func. Instead it would be easier to deal with if it was in it's own class (my opinion).

This is of course creating more classes for you to maintain but trying to make it more testable.

In short, yes you can but you should choose an approach that matches your situation and needs for your project. I like the dependency injection approach because it is testable, readable and easy to expand into something more complex without changing the primary code, but always go with what you feel comfortable with as well as what is the best for your project / team.

Related Topic