2013-11-09 244 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-beferred(不是完全相同的問題,但Eric Lippert的答案也適用於你的問題)。 – haim770

回答

2

publish接口簽名定義2層的約束:

void Publish<TEvent, TPayload>(TEvent @event) where TEvent : IEvent<TPayload>; 
  1. TEvent這將是該方法參數簽名
  2. TPayload其中指出TEvent必須實現的接口IEvent<TPayload>

如果發佈方法只有的約束,則編譯器可以推斷的使用,因爲已經定義了類型約束的方法的簽名,即,

void Publish<TEvent>(TEvent @event); 

上面然後將允許方法調用,而不類型約束將被使用,即,

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

然而,由於接口定義了第二個限制,編譯器不知道的意圖是什麼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(); 
}