2016-03-03 3 views
1

Предположим, у меня есть следующий интерфейс:Не удается преобразовать из реферата <Generic> в бетон: Аннотация <Бетон: Generic>

public interface IMessageProcessor<T> where T: BaseMessage { 
     void Process(T msg); 
    } 

У меня есть абстрактный класс, который реализует этот интерфейс:

public abstract class AMessageProcessor<T> : IMessageProcessor<T> where T : BaseMessage { 

     protected Controller Controller { get; private set; } 

     public AMessageProcessor(Controller controller) { 
      Controller = controller; 
     } 

     public abstract void Process(T msg); 
    } 

то у меня есть сообщение:

public class RRMessage : BaseMessage { 
    ... 
} 

, а затем у меня есть реализация:

public class RRMessageProcessor : AMessageProcessor<RRMessage> { 
     public RRMessageProcessor(Controller controller) : base(controller) {} 

     public override void Process(RRMessage msg) { 

      //do something here 
     } 
    } 

Теперь в другом классе я хотел бы сделать список этих процессоров для различных сообщений:

public readonly List<AMessageProcessor<BaseMessage>> AvailableMessageProcessors; 

     public MessageProcessingStrategy(Controller controller) { 
      AvailableMessageProcessors = new List<AMessageProcessor<BaseMessage>>(); 

      /* ----- ERROR HAPPENS AT THIS LINE ------ */ 
      AvailableMessageProcessors.Add(new RRMessageProcessor(controller)); 

     } 

И я получаю эту ошибку:

Error CS1503 Argument 1: cannot convert from 'RRMessageProcessor' to 'AMessageProcessor<BaseMessage>'

кажется, что конверсия должна работа ... Почему он не может его преобразовать? Как я могу заставить его работать?

+0

Нет безопасного пути для этого. Если это разрешено, вы можете передать «BaseMessage» в «RRMessage.Process», для которого требуется параметр «RRMessage». – Lee

ответ

1

У меня была проблема в моей другой ответ (удален) о ковариантных типов в параметрах, но подход, как это может решить вашу проблему:

  1. Определяет a BaseMessageProcessor класс (может быть AMessageProcessor), как этот:

    public abstract class BaseMessageProcessor 
    { 
        protected Controller Controller { get; private set; } 
    
        public BaseMessageProcessor(Controller controller) 
        { 
         Controller = controller; 
        } 
    
        public void Process<T>(T msg) where T : BaseMessage 
        { 
         if (this is IMessageProcessor<T>) 
          (this as IMessageProcessor<T>).Process(msg); 
    
         throw new NotSupportedException(); 
        } 
    } 
    
  2. Определяет интерфейс IMessageProcessorOf<T>:

    public interface IMessageProcessor<T> where T : BaseMessage 
    { 
        void Process(T msg); 
    } 
    
  3. определяет конкретные процессоры Наследование BaseMessageProcessor и реализующие (явно) IMessageProcessorOf<T>:

    public class RRMessageProcessor : BaseMessageProcessor, IMessageProcessorOf<RRMessage> 
    { 
        public RRMessageProcessor(Controller controller) : base(controller) { } 
    
        void IMessageProcessor<RRMessage>.Process(RRMessage msg) 
        { 
         ... 
        } 
    } 
    

Это решение позволяет работать с AvailableMessageProcessors:

public List<BaseMessageProcessor> AvailableMessageProcessors; 

... 

AvailableMessageProcessors = new List<BaseMessageProcessor>(); 
AvailableMessageProcessors.Add(new RRMessageProcessor(controller)); 

Итак, если у вас есть 2 типа сообщения, как RRMessage и SSMessage, вы можете определить один MultiMessageProcessor:

public class MultiMessageProcessor : BaseMessageProcessor, IMessageProcessorOf<RRMessage>, IMessageProcessorOf<SSMessage> 
{ 
    public MultiMessageProcessor(Controller controller) : base(controller) { } 

    void IMessageProcessorOf<RRMessage>.Process(RRMessage msg) 
    { 
     ... 
    } 

    void IMessageProcessorOf<SSMessage>.Process(SSMessage msg) 
    { 
     ... 
    } 
} 

Призывы Process() способом будет осуществляться через BaseMessageProcessor.Process<>:

multiProcessor.Process(new RRMessage()); 
multiProcessor.Process(new SSMessage()); 

Или просто использовать RRMessageProcessor и определите SSMessageProcessor, используя ту же идею, что и раньше.

+0

awesome! Это отлично работает! и мне нравится гибкость в обработке нескольких сообщений в одном MessageProcessor! – Denis

0

Это не самый чистый/самый красивый способ сделать это, но, похоже, он работает. Вот что я изменил в AMessageProcessor:

public abstract class AMessageProcessor : IMessageProcessor<BaseMessage> 

     protected Controller Controller { get; private set; } 

     public AMessageProcessor(Controller controller) { 
      Controller = controller; 
     } 

     /* ----- REALLY DON'T WANT TO OVERRIDE THIS METHOD EVERYWHERE --- */ 
     public abstract void Process(BaseMessage msg); 
    } 

А затем изменить RRMessageProcessor как:

public class RRMessageProcessor : AMessageProcessor, IMessageProcessor<RRMessage> { 
     public RRMessageProcessor(Controller controller) : base(controller) {} 

     /* ----- REALLY DON'T WANT TO OVERRIDE THIS METHOD LIKE THIS EVERYWHERE --- */ 
     public override void Process(BaseMessage msg) { 
      Process(msg as RRMessage); 
     } 

     public void Process(RRMessage msg) { 

      //do something here 
     } 
    } 
+0

Вы можете избежать этого, создав класс-оболочку 'ProcWrapper : IMessageProcessor ', который содержит 'IMessageProcessor ' и выполняет листинг в методе' Process'. Это лучшее, что вы можете сделать, поскольку этот подход в любом случае небезопасен. – Lee

-1

Вы можете легко решить вашу проблему путем переопределения

public readonly List<AMessageProcessor<BaseMessage>> AvailableMessageProcessors;

в

public readonly List<RRMessageProcessor> AvailableMessageProcessors;

Так у вас нет проблем, литых и вы с помощью вы пользовательские объекты

надежда на помощь!

+0

Но мой список будет содержать разные процессоры сообщений, а не только RRMessageProcessor - это всего лишь один из процессоров. В моем списке будут другие процессоры сообщений, которые будут наследоваться от AMessageProcessor – Denis

+0

, и посмотрите на это: http://stackoverflow.com/questions/17506587/classes-hierarchy-and-casting-between-objects – user2543740

0

На вашем общем типе вам необходимо определить тип как ковариантный.См https://msdn.microsoft.com/en-us/library/dd997386.aspx

Это должно работать

public interface IMessageProcessor<in T> where T : BaseMessage 
{ 
    void Process(T msg); 
} 
+0

утра, получая ту же ошибку. Вы можете показать полный код? (не только это изменение) – Denis

Смежные вопросы