Search code examples
c#.netgenericsdynamic-dispatch

Accessing a method which was assigned to a passed in Func<> parameter


Looks like I've missed something obvious but really can't figure out why I can't use/access a method which was assigned to a passed in Func<> parameter, I want to access/call it in the external calling context.

Below the simple example: (real results data type is not string but some complex type)

class Caller
{
    private Func<string> factory;

    public Caller()
    {
        this.factory = null;
        var adapter = new Adapter(this.factory);
        adapter.Adapt(...);

        // then I would share this factory via IoC
        dependencyContainer.RegisterUsingFactoryMethod<string>(this.factory);

        // !!! but here is factory still not assigned
        string results = this.factory();
    }
}

class Adapter
{
    private string results;

    public Adapter(Func<string> factory)
    {
        factory = () => this.results;
    }

    public IOut Adapt(IIn input)
    {
       this.results = someCalculatedData;
    }
}

Solution

  • This has nothing to do with delegates, and everything to do with the fact that all arguments are passed by value by default. You'd see the same behaviour if factory were an int, or a string, or a StringBuilder.

    If you want your Service constructor to modify the value of the argument, you need a ref or out parameter. However, I would personally suggest that you change it to use a method which returns a Func<string>:

    public class Caller
    {
        private readonly Func<string> factory;
    
        public Caller()
        {
            this.factory = Service.GetFunc();
        }
    }
    
    public class Service
    {
        public static Func<string> GetFunc()
        {
            return () => Guid.NewGuid().ToString();
        }
    }
    

    (There are various other options here, such as a method group conversion, or a property. It's hard to know what to recommend when we don't have much context.)

    Obviously you'd want to rename the method. But calling a constructor solely to modify an out parameter would be a very odd bit of code.

    For more on parameter passing, see my article on the topic.