Passing a member-access function in the constructor

Viewed 60

Let's say I have a helper class like

public class Selection<T, W> : ISelection<T,W> where W : ICollection<T>
{
    public Selection(Func<W> selectableItemsProvider)
    {
        ...
    }
}

As C# does not support delegating interface implementation, I thought I'd save myself a bit of boilerplate and just extend the Selection class...

public class MyFoo : Selection<Blah, List<Blah>>
{
    private List<Blah> _blahs = new List<Blah>();


    public MyFoo() : base(() => _blahs)
    {
        ...
    }
}

Except I cannot do that, can I, because

Cannot access non-static property '_blahs' in static context

even though I already know the provider won't be invoked until after object construction.

Is there a way around this or am I stuck with adding boilerplate code?

3 Answers

One approach might be to first introduce base abstract class. This class will hold all the logic:

public abstract class SelectionBase<T, W> where W : ICollection<T>
{
    protected SelectionBase() {

    }

    protected abstract Func<W> GetSelectableItemsProvider();
}

Then change your current Selection to inherit from it and accept selector in constructor, that's the whole implementation:

public sealed class Selection<T, W> : SelectionBase<T, W> where W: ICollection<T> {
    private readonly Func<W> _selectableItemsProvider;

    public Selection(Func<W> selectableItemsProvider) {
        _selectableItemsProvider = selectableItemsProvider;
    }

    protected override Func<W> GetSelectableItemsProvider() {
        return _selectableItemsProvider;
    }
}

Now if you want to extend selection in a way mentioned in your question:

public class SpecificSelection : SelectionBase<string, List<string>> {
    private List<string> _blah;

    public SpecificSelection() {

    }

    protected override Func<List<string>> GetSelectableItemsProvider() {
        return () => _blah;
    }
}

One somewhat roundabout way to do this is to pass a lambda that returns the same list every time to the base class constructor. Then call that lambda to initialise blahs:

public class Selection<T, W>: ISelection<T, W> where W : ICollection<T> {

    // assuming you have a property like this that is initialised by the constructor...
    protected Func<W> SelectableItemsProvider { get; }
    public Selection(Func<W> selectableItemsProvider)
    {
        SelectableItemsProvider = selectableItemsProvider;
    }
}

public class MyFoo : Selection<Blah, List<Blah>>
{
    private List<Blah> _blahs;


    public MyFoo() : base(new List<Blah>().CaptureInFunc())
    {
        _blahs = SelectableItemsProvider();
    }
}

CaptureInFunc is an extension method declared like this:

public static Func<T> CaptureInFunc<T>(this T t) => () => t;

This gives us a lambda that returns t every time.

You can't refer to members of 'this' in the call to a base constructor because 'this' does not exist yet.

I suggest composition instead of inheritance. The class may still implement the same interface as the helper and then delegate all calls to the real implementation.

public class MyFoo : ISelection<Blah, List<Blah>>
{
    private List<Blah> _blahs;
    
    private ISelection<Blah, List<Blah>> _implementation;

    public MyFoo()
    {
        _blahs = new List<Blah>();
        _implementation = new Selection<Blah, List<Blah>>(() => _blahs);
    }
    
    public void DoSomethingWithSelection()
    {
        _implementation.DoSomethingWithSelection();
    }
}
Related