2013-11-09 2 views
3

Я всегда был под впечатлением, что C# компилятор может сделать вывод Params типа в тех случаях, как следующие:C# 5.0 Обобщения: парам типа умозаключение

class Program 
{ 
    static void Main(string[] args) 
    { 
     IMessageBus messageBus = null; 

     //Here the compiler nags "type params for Publish cannot be inferred from the usage.. .." 
     messageBus.Publish(new CorrectorAdded(10)); 
    } 
} 

public interface IEvent<out TPayload> 
{ 
    TPayload Payload { get; } 
} 

public abstract class EventBase<TPayload> : IEvent<TPayload> 
{ 
    public TPayload Payload { get; private set; } 

    protected EventBase(TPayload payload) 
    { 
     Payload = payload; 
    } 
} 

public interface IMessageBus 
{ 
    void Publish<TEvent, TPayload>(TEvent @event) where TEvent : IEvent<TPayload>; 

    IDisposable Subscribe<TEvent, TPayload>(Action<TPayload> listener) where TEvent : IEvent<TPayload>; 
} 

public class CorrectorAdded : EventBase<CorrectorAddedArgs> 
{ 
    public CorrectorAdded(CorrectorAddedArgs payload) : base(payload) 
    { 
    } 

    public CorrectorAdded(int correctorId) : this(new CorrectorAddedArgs(correctorId)) 
    { 
    } 
} 

public class CorrectorAddedArgs 
{ 
    public int CorrectorId { get; private set; } 

    public CorrectorAddedArgs(int correctorId) 
    { 
     CorrectorId = correctorId; 
    } 
} 

Любые идеи о том, почему это происходит и как получить вывод типа для работы в этом случае?

Спасибо.

+2

Возможный дубликат http://stackoverflow.com/questions/6630690/wh y-cant-nested-generic-types-be-inferred (не тот же самый вопрос, но ответ Эрика Липперта также относится к вашему вопросу). – haim770

ответ

2

Интерфейс подписи для publish определяет 2 ограничения:

void Publish<TEvent, TPayload>(TEvent @event) where TEvent : IEvent<TPayload>; 
  1. TEvent, которые будут подписи для параметра метода
  2. TPayload который гласит TEvent должен реализовать интерфейс IEvent<TPayload>

Если метод публикации имеет только ограничение TEvent, то компилятор может вывести использование с подписью метода уже имеет тип ограничения определены, т.е.

void Publish<TEvent>(TEvent @event); 

выше позволит затем для вызова метода, которые будут использоваться без типа ограничений, т.е.

messageBus.Publish(new CorrectorAdded(10)); 
// would be the same as 
messageBus.Publish<CorrectorAdded>(new CorrectorAdded(10)); 

Однако, поскольку интерфейс определяет 2-го ограничения, компилятор не имеет ни малейшего представления о том, что цель заключается в TPayload, поскольку событие может реализовать любое количество IEvent<TPayload> «с. Однако, если метод подпись действительно включает в себя тип TPayload то компилятор может фактический вывод ограничения, то есть,

void Publish<TEvent, TPayload>(TEvent @event, TPayload payload) where TEvent : IEvent<TPayload>; 

И тогда тогда метод может быть вызван без ограничений типа:

messageBus.Publish(new CorrectorAdded(10), new FooThatImplementsTPayload()); 
+1

Спасибо! Это прояснилось. Не облегчил мою жизнь, хотя API выглядит так неоднозначно =). Нужно будет что-то обернуть вокруг ... – Ant

0

К те, кто все еще интересно, как альтернативный API может выглядеть, вот что я придумал:

class Program 
{ 
    static void Main(string[] args) 
    { 
     IMessageBus messageBus = null; 

     messageBus 
      .Event<CorrectorAdded>() 
      .Subscribe(eventArgs => { /*skipped*/ }); 

     //skipped 

     messageBus 
      .Event<CorrectorAdded>() 
      .Publish(new CorrectorAddedArgs(1)); 
    } 
} 

public abstract class EventBase 
{ 
    //skipped 
} 

public abstract class EventBase<TPayload> : EventBase 
{ 
    public IDisposable Subscribe(Action<TPayload> listener) 
    { 
     //skipped 
    } 

    public void Publish(TPayload payload) 
    { 
     //skipped 
    } 
} 

public class CorrectorAdded : EventBase<CorrectorAddedArgs> 
{ 
} 

public class CorrectorAddedArgs 
{ 
    public int CorrectorId { get; private set; } 

    public CorrectorAddedArgs(int correctorId) 
    { 
     CorrectorId = correctorId; 
    } 
} 

public interface IMessageBus 
{ 
    TEvent Event<TEvent>() where TEvent : EventBase, new(); 
} 
Смежные вопросы