无法将对象添加到列表中

时间:2010-11-02 22:37:42

标签: c# oop interface

我正在尝试使用Domain Events来通知系统中发生了什么事情(借鉴herehere)。

我真的很接近让代码按照我想要的方式工作,但是,我已经碰到了一些砖墙。这是我的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>类型,而不是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; }
    }

这是我没有得到的 - 如果CustomerHasUnpaidDuesEvent实施IDomainEvent,那么为什么对Actions.Add的调用失败?我该如何解决这个问题?

编辑:

为了使事情更清楚,这里是我的测试应用程序的完整代码:

    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 个答案:

答案 0 :(得分:1)

编辑:
问题是,为了让IEventHandler<CustomerHasUnpaidDuesEvent>位于IEventHandler<IDomainEvent>列表中,我们需要T作为IEventHandler<T>中的协变模板参数(声明为IEventHandler<out T>)。但是,为了允许函数Handle(T arg),我们需要T 逆变。严格来说,这种方式不起作用。想象一下:如果我们真的可以将IEventHandler<CustomerHasUnpaidDuesEvent>插入到IEventHandler<IDomainEvent>的列表中,那么有人可能会尝试使用某种类型的参数调用Handle,而这些参数来自IDomainEvent但是不是CustomerHasUnpaidDuesEvent!这应该是不可能的。

解决方案是我们不需要Register处的确切类型,因此我们可以保留对通用基接口的引用。实施在这里:http://ideone.com/9glmQ

旧答案无效,请保持一致以保持一致。


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

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

修改:肯定CustomerHasUnpaidDuesEventIDomainEvent,但您需要IEventHandler<CustomerHasUnpaidDuesEvent>成为IEventHandler<IDomainEvent>。这正是协方差所做的。为了实现这一点,必须将IEventhandler中的模板参数声明为covariant(<in T>而不是<T>)。

答案 1 :(得分:1)

您的Register方法不需要通用:

    public static void Register(IEventHandler<IDomainEvent> callback)
    {
        if (Actions == null)
        {
            Actions = new List<IEventHandler<IDomainEvent>>();
        }
        Actions.Add(callback);
    }