简体   繁体   中英

Specifying method with interface parameter

I spent some time learning how "Event Sourcing" works and how one would implement that in C# and i got stuck at some point. As it is very difficult to describe my problem without code I'll first give you a simplified version of my code. I removed all the unneeded code and left the relevant parts.

public interface IEvent { }
public class UserCreated : IEvent { }
public class UserDeleted : IEvent { }

public interface IEventSourcable
{
    ICollection<IEvent> History { get; }
    void ApplyEvent(IEvent e);
}

public abstract class EntityBase : IEventSourcable
{
    public ICollection<IEvent> History { get; }
    public void ApplyEvent(IEvent e)
    {
        History.Add(e);
    }
}

public class User : EntityBase
{
    public void ApplyEvent(UserCreated e)
    {
        base.ApplyEvent(e)
    }
}

What I would like to do is to prevent the base method to be used if a matching method is not implemented ie

User u = new User();
u.ApplyEvent(new UserCreated());

should work and call the method in User (which it does) but

u.ApplyEvent(new UserDeleted()); 

should not call the base method but give an error at compile time.

I've seen different approaches which would give a runtime error or simply ignore the problem if a matching method is not implemented like

  1. Simply override the method and check the type

     public class User : EntityBase { public override void ApplyEvent(IEvent e) { if (e is UserCreated) ApplyEvent((UserCreated)e); else if (e is UserDeleted) ApplyEvent((UserDeleted)e); else throw new UnknownEventException(); // Or handle it however } } 
  2. Use the dynamic operator

     public abstract class EntityBase : IEventSourcable { public ICollection<IEvent> History { get; } public void ApplyEvent(IEvent e) { History.Add(e); ((dynamic)this).Apply((dynamic)e); } } public class User : EntityBase { public override void Apply(UserCreated e) { // do something } } 

I know I could do it in either one of mentioned ways but I'm more interested in whether what I'm thinking of is possible or not.

You could implement the interface explicitly, which would prevent unwanted event types on a concrete instance:

public abstract class EntityBase : IEventSourcable
{
    ICollection<IEvent> IEventSourcable.History { get; }

    void IEventSourcable.ApplyEvent(IEvent e)
    {
        // Do the magic
    }

    protected void ApplyEvent(IEvent e)
    {
        (this as IEventSourcable).ApplyEvent(e);
    }
}

public class User : EntityBase
{
    public void ApplyEvent(UserCreated e)
    {
        base.ApplyEvent(e);
    }
}

However that wouldn't prevent a consumer from casting to an IEventSourcable and adding arbitrary events:

IEventSourcable u = new User();
u.ApplyEvent(new UserCreated());
u.ApplyEvent(new UserDeleted());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM