代码之家  ›  专栏  ›  技术社区  ›  Jason Evans

无法将对象添加到列表

  •  0
  • Jason Evans  · 技术社区  · 14 年前

    我在试一个使用 Domain Events 通知系统中发生了什么事情(从 here here ).

    我真的很接近让代码按我想要的方式工作,但是,我碰到了一个砖墙位。这是我的 DomainEvents 班级:

     public static class DomainEvents
        {
            [ThreadStatic]
            private static IList<IEventHandler<IDomainEvent>> Actions;
    
            public static void Register<T>(IEventHandler<T> callback) where T : IDomainEvent
            {
                if (Actions == null)
                {
                    Actions = new List<IEventHandler<IDomainEvent>>();
                }
    
                Actions.Add(callback); // <---- Problem here, since I can't add callback to the collection.
            }
    
            public static void ClearCallbacks()
            {
                Actions = null;
            }
    
            public static void Raise<T>(T args) where T : IDomainEvent
            {
                if (Actions == null)
                {
                    return;
                }
    
                foreach (var action in Actions)
                {
                    if (action is IEventHandler<T>)
                    {
                        ((IEventHandler<T>)action).Handle(args);
                    }
                }
            }
    

    以上内容无法编译,因为 Actions.Add callback 因为这是一个 IEventHandler<T> 键入而不是a IEventHandler<IDomainEvent> 类型。这里还有一些代码需要澄清。

    这是从我的控制台应用程序调用的:

    DomainEvents.Register(new CustomerHasUnpaidDuesEventHandler());

    CustomerHasUnpaidDuesEventHandler 工具 IEventHandler<CustomerHasUnpaidDuesEvent> ,在哪里 CustomerHasUnpaidDuesEvent IDomainEvent

    public class CustomerHasUnpaidDuesEventHandler : IEventHandler<CustomerHasUnpaidDuesEvent>
        {
            public IEmailSender EmailSender { get; set; }
    
            public void Handle(CustomerHasUnpaidDuesEvent @event)
            {
                this.EmailSender.SendEmail(@event.Customer.EmailAddress);
            }
        }
    
    public class CustomerHasUnpaidDuesEvent : IDomainEvent
        {
            public CustomerHasUnpaidDuesEvent(Customer customer)
            {
                this.Customer = customer;
            }
    
            public Customer Customer { get; set; }
        }
    

    这就是我不明白的-如果 客户支付 IDomain事件 ,那为什么打电话给 操作。添加 弱点?我如何解决这个问题?

    编辑:

    为了让事情更清楚,下面是我的测试应用程序的完整代码:

        class Program
        {
            static void Main()
            {
                DomainEvents.Register(new CustomerHasUnpaidDuesEventHandler());
    
                var c = new Customer();
    
                c.EmailAddress = "test@dfsdf.com";
    
                c.CheckUnpaidDues();
            }
        }
    
    
     public interface IEventHandler<in T> where T : IDomainEvent
        {
            void Handle(T args);
        }
    
     public interface IEmailSender
        {
            void SendEmail(string emailAddress);
        }
    
    
     public interface IDomainEvent
        {
        }
    
    public static class DomainEvents
        {
            [ThreadStatic]
            private static IList<IEventHandler<IDomainEvent>> Actions;
    
            public static void Register<T>(IEventHandler<T> callback) where T: IDomainEvent
            {
                if (Actions == null)
                {
                    Actions = new List<IEventHandler<IDomainEvent>>();
                }
    
                Actions.Add(callback);
            }
    
            public static void ClearCallbacks()
            {
                Actions = null;
            }
    
            public static void Raise<T>(T args) where T : IDomainEvent
            {
                if (Actions == null)
                {
                    return;
                }
    
                foreach (IEventHandler<T> action in Actions)
                {
                    (action).Handle(args);
                }
            }
        }
    
    
    public class CustomerHasUnpaidDuesEventHandler : IEventHandler<CustomerHasUnpaidDuesEvent>
        {
            public IEmailSender EmailSender { get; set; }
    
            public void Handle(CustomerHasUnpaidDuesEvent @event)
            {
                this.EmailSender.SendEmail(@event.Customer.EmailAddress);
            }
        }
    
    public class CustomerHasUnpaidDuesEvent : IDomainEvent
        {
            public CustomerHasUnpaidDuesEvent(Customer customer)
            {
                this.Customer = customer;
            }
    
            public Customer Customer { get; set; }
        }
    
    public class Customer
        {
            public string Name { get; set; }
    
            public string EmailAddress { get; set; }
    
            public bool HasUnpaidDues { get; set; }
    
            public void CheckUnpaidDues()
            {
                HasUnpaidDues = true;
                DomainEvents.Raise(new CustomerHasUnpaidDuesEvent(this));
            }
        }
    

    干杯。

    2 回复  |  直到 4 年前
        1
  •  1
  •   Robert Jeppesen    14 年前

    您的Register方法不需要是泛型的:

        public static void Register(IEventHandler<IDomainEvent> callback)
        {
            if (Actions == null)
            {
                Actions = new List<IEventHandler<IDomainEvent>>();
            }
            Actions.Add(callback);
        }
    
        2
  •  1
  •   Vlad    14 年前

    编辑:
    IEventHandler<CustomerHasUnpaidDuesEvent> 被列入名单 IEventHandler<IDomainEvent> T 作为中的协变模板参数 IEventHandler<T> IEventHandler<out T> ). 但是为了允许 Handle(T arg) T型 . 严格说来,这种方法行不通。想象一下:如果我们真的能插入 IEventHandler<客户HaunPaidueSevent> 进入一个列表 IEventHandler<IDomainEvent> s、 可能有人会打电话来 Handle IDomainEvent 但不是一个 CustomerHasUnpaidDuesEvent ! 这应该是不可能做到的。

    解决方法是我们不需要精确的类型 Register ,因此我们可以保留对泛型基接口的引用。具体实施如下: http://ideone.com/9glmQ


    也许你需要声明IEventHandler接受T作为协变类型?

    interface IEventHandler<in T> where T: IDomainEvent
    {
        void Handle();
        // ...
    }
    

    IDomain事件 ,但你需要 IEventHandler<客户HaunPaidueSevent> 成为一个 IEventHandler<IDomainEvent> . 这正是协方差的作用。为了允许这样做,IEventhandler中的模板参数必须声明为协变的( <in T> 而不仅仅是 <T>