Search code examples
c#system.reflection

Calling parent method dynamically from base fails with inaccessible due to its protection level


I am trying to dynamically call a method on my parent from my base class via (this as dynamic).When(e) but i am getting an error regarding protection level:

An unhandled exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Core.dll

Additional information: 'Person.When(PersonCreated)' is inaccessible due to its protection level

The Person class is public, and changing the Person.When(PersonCreated) to public gives a new error The best overloaded method match for 'Person.When(PersonCreated)' has some invalid arguments...

What I want to achieve is to 'route' events to the parent class's When(e) methods via the base Apply(e) method.

Ideally the When method in the base should not be public.

I'm pulling my hair out no doubt doing something very stupid... any ideas please or do I need to use reflection instead?

public abstract class EventSourcedAggregate
{
    readonly List<DomainEvent> mutatingEvents = new List<DomainEvent>();
    public readonly int UnmutatedVersion;

    protected void Apply(DomainEvent e)
    {
        this.mutatingEvents.Add(e);

        // the below line throws inaccessible protection level
        (this as dynamic).When(e);
    }
}

public abstract class DomainEvent
{
    // snipped some time stamp stuff not relevant here
}

public class PersonCreated : DomainEvent
{
    public readonly string Name;
    public readonly string Address;

    public PersonCreated(string name, string address)
    {
        Name = name;
        Address = address;
    }
}

public class Person : EventSourcedAggregate
{
    public Person(string name, string address)
    {
        Apply(new PersonCreated(name, address));
    }

    public string Name { get; private set; }
    public string Address { get; private set; }

    void When(PersonCreated e)
    {
        Name = e.Name;
        Address = e.Address;
    }
}

static void Main(string[] args)
{
    var user = new Person("sir button", "abc street");
}

Solution

  • Note that you get a different exception when the When() method is made public. I.e. it's now accessible, but you are not passing the correct type.

    The When() method as declared requires an instance of PersonCreated. But your call site is only passing an instance of DomainEvent.

    In general, dynamic defers to runtime behaviors that would normally be done at compile time. But otherwise, you have to follow the same rules. Since the compiler can't guarantee that DomainEvent is an instance of PersonCreated, you get the error (or more specifically, DomainEvent doesn't match a known overload of the method).

    You should be able to get the code to work by calling like this instead:

    (this as dynamic).When(e as dynamic);
    

    I.e. let the runtime bind based on the dynamic type of e instead of its static type.