2011-11-02 102 views
4

我試圖創建一個事件委託其中參數是強類型匹配當前類靜態定義的成員,像這樣:使用動態確定類類型

public class HPCRequest 
{ 
    public delegate void RequestCompleteHandler(HPCRequest request); 
    public event RequestCompleteHandler RequestComplete; 

的問題是,該點這個類是要繼承的,我真正想要的是所有這些繼承類有「RequestComplete」事件,其中委託類型的那個類:

public class HPCGetConfig : HPCRequest 
{ 
    //I want this class to effectively inherit something like this: 
    //public delegate void RequestCompleteHandler(HPCGetConfig request); 

這是因爲,目前,當我有一個函數來處理我的一個「RequestCompl ETE」事件,我現在必須這樣做:

myGetConfigRequest.RequestComplete += new HPCRequest.RequestCompleteHandler(HPCGetExpectedLosses_RequestComplete); 

void HPCGetConfig_RequestComplete(HPCRequest request) 
{ 
    HPCGetConfig thisRequest = request as HPCGetConfig; 
    //This should be strongly typed in the first place. 

但我希望能夠做這樣的事情:

request.RequestComplete += new HPCGetConfig.RequestCompleteHandler(HPCGetConfig_RequestComplete); 
    request.SendRequestAsync(); 
} 

void HPCGetConfig_RequestComplete(HPCGetConfig request) 
{ 
    request.RequestComplete -= HPCGetConfig_RequestComplete; 

嘗試

我已經嘗試這樣:

public delegate void RequestCompleteHandler<T>(T request) where T : HPCRequest; 
public event RequestCompleteHandler<T> RequestComplete; 

但是當我嘗試使用RequestComplete(this);基類中調用的情況下,我得到一個編譯時錯誤:'委託「RequestCompleteHandler」有一些無效的參數。

發生這種情況是否我設置整個HPCRequest類作爲HPCRequest<T>轉到:RequestComplete(this);

 

public class HPCRequest<T> where T : HPCRequest<T> 
{ 
    public delegate void RequestCompleteHandler<T>(T request); 
    public event RequestCompleteHandler<T> RequestComplete; 

public class HPCGetConfig : HPCRequest<HPCGetConfig> { ... 

當我嘗試調用事件發生同樣的錯誤我也嘗試過所有形式的創建委託和事件並覆蓋它們,例如在做:

public class HPCRequest 
{ 
    public delegate void RequestCompleteHandler(HPCRequest request); 
    public virtual event RequestCompleteHandler RequestComplete; 


public sealed class HPCGetConfig : HPCRequest 
{ 
    public delegate void RequestCompleteHandler(HPCGetConfig request); 
    public override event RequestCompleteHandler RequestComplete; 

但是,這給了我一個編譯時錯誤,因爲我不能用不同的委託類型的一個覆蓋RequestComplete事件。

還有其他想法嗎?


編輯

模板整個HPCRequest類是不是一種選擇,一個很徹底的嘗試之後,我發現它只是搞砸了一切努力,以使用類型HPCRequest作爲一個佔位符的任何請求類型。如果此解決方案正常工作,則類HPCRequest必須能夠實例化並繼承而不指定類型參數。我將需要一個不需要模板的解決方案HPCRequest

爲了確保每個人都知道我究竟如何我試圖用這個,我粘貼一些示例代碼到引擎收錄的是應該讓你實驗,獲得本次活動的模板不破壞任何東西的工作方式。這裏是:http://pastebin.com/bbEYgLj1

回答

0

它不能做

我已經結束了不得不在任何地方使用它的代表。

如果有任何其他表現出來,很樂意改變接受的答案。

2

您可以使用泛型類型參數用於此目的,因此每個繼承自HPCRequest的類都必須指定泛型類型參數。此外,我會建議標記被設計爲一個基類的abstract修改,以避免明確的實例化類:

public abstract class HPCRequest<TRequest> 
    where TRequest: class 
{ 
    public delegate void RequestCompleteHandler(TRequest request); 
    public event RequestCompleteHandler<TRequest> RequestComplete; 
} 


public sealed class HPCGetConfig : HPCRequest<HPCGetConfig> 
{ 
} 
+0

這似乎是朝着正確方向邁出的一步,但假設HPCRequest是抽象的是錯誤的。該類的** main **點將被繼承,但它可以被實例化,並且包含某些方法的實現。當我嘗試使用下列方法調用它的RequestComplete事件:'if(RequestComplete!= null)RequestComplete(this);' - 我得到一個錯誤:'Delegate RequestCompleteHandler有一些無效的參數。' – Alain

+0

我可以支持基類嗎?定義如下:'public class HPCRequest:HPCRequest {}'? \ *編輯\ *我不這麼認爲,因爲那時我的其他類不能再轉換成這個類。 – Alain

+0

如果您想跟進,我已經編輯了這個問題。 – Alain

2

可以約束抽象類的類型參數從抽象類本身派生。這似乎很奇怪,但你可以將它設置這樣的:

public abstract class HPCRequest<T> where T : HPCRequest<T> 
{ 
    public delegate void RequestCompleteHandler(T request); 
    public event RequestCompleteHandler RequestComplete; 
} 

public class DerivedHPCRequest : HPCRequest<DerivedHPCRequest> 
{ 

} 
+0

這是在我使用基類的時候破壞了我的代碼,例如'HPCRequest anyRequestHolder = ...'我並不總是希望強類型化HPCRequest的實例。 – Alain

+0

如果您想跟進,我已經編輯了這個問題。 – Alain

3

你可以嘗試什麼:

public abstract class HPCRequest<T> where T : HPCRequest<T> 
{ 
    public delegate void RequestCompleteHandler(T request); 
    public event RequestCompleteHandler RequestComplete; 

    protected void RaiseRequestComplete(T request) 
    { 
    if (RequestComplete != null) 
    { 
     RequestComplete(request); 
    } 
    } 
} 

public class Foo : HPCRequest<Foo> 
{ 
    public void Bar() 
    { 
    RaiseRequestComplete(this); 
    } 
} 

public class Example 
{ 
    public static void Test() 
    { 
    var request = new Foo(); 

    request.RequestComplete += RequestComplete; 
    } 

    static void RequestComplete(Foo request) 
    { 
    // It's a Foo! 
    } 
} 

這種自我指涉的通用的限制讓你想要什麼,我認爲。我添加了protected RaiseRequestCompleted,所以您仍然可以從繼承自HCPRequest的類中引發事件。否則,只允許HCPRequest這樣做。

UPDATE:我更新了代碼以通過this並添加了與您所需結果相匹配的示例代碼。

+1

我認爲委託和事件的類型參數是不必要的,實際上與類的類型參數衝突。 – Jay

+0

是的,你是對的。我複製粘貼從問題:) – JulianR

+0

看到類似的建議。 'RequestComplete(this);'給我一個錯誤。我的結構不支持'RaiseRequestComplete'。我不能像你這樣做'新Foo()',因爲HPCRequest實例需要將自己作爲事件參數傳遞。 – Alain

0

我認爲你必須創建一個能夠造成的不能被實例化一個抽象基類:

public abstract class HPCRequestBase<T> where T : HPCRequestBase<T> 
{ 
    public delegate void RequestCompleteHandler(T request); 
    public event RequestCompleteHandler RequestComplete; 

    protected void OnRequestComplete(T request) 
    { 
     if (RequestComplete != null) { 
      RequestComplete(request); 
     } 
    } 

    public void Test() 
    { 
     OnRequestComplete((T)this); 
    } 

} 

public class HPCRequest : HPCRequestBase<HPCRequest> 
{ 
    public void Test2() 
    { 
     OnRequestComplete(this); 
    } 
} 

public class HPCRequestConfig : HPCRequestBase<HPCRequestConfig> 
{ 
    // Derived from the base class, not from HPCRequest 
} 

而且「這個」必須被強制轉換爲T:OnRequestComplete((T)this);

此測試沒有一個錯誤:

var hpcr = new HPCRequest(); 
hpcr.Test(); 
hpcr.Test2(); 
+0

其他一些建議的重複類型,但主要的問題是基類是抽象的,所以我不能將實現(如'HPCRequestConfig')轉換爲'HPCRequestBase '並調用虛擬方法。這個功能是對那些不知道非泛型類型的各種HPCRequests起作用的函數所要求的。您可以查看附加到問題的pasteBin代碼,以瞭解它是如何使用的。 – Alain

+0

你不能轉換到一個開放的泛型基類。你可以擁有什麼,是一個非泛型的基類。然後派生特定的類並讓它們實現一個通用接口。您還可以通過構造函數注入具有特定類型的東西,因爲構造函數不是基類或接口的一部分。 –

1

你的使用情況是在一個通用的鹼基C明確地執行非通用接口一個偉大的候選人lass。我不確定我是否完全理解了所需的功能,但我認爲我做到了。我寫了一些應該讓你開始的代碼(如下)。

作爲一個方面說明,沒有真正的理由宣佈代表。爲了與MSDN Event Design standards保持一致,您的解決方案將使用EventHandler<T>和自定義EventArgs實現。

代碼

// this is a sample program using the pattern i am recommending 
// I'm pretty sure this is what you wanted your code to look like? 
public class Program 
{ 
    public static void Main() 
    { 
     var request = new HPCGetConfig(); 
     request.RequestComplete += HandleConfigRequestCompleted; 
     request.SendAsync(); 
    } 

    static void HandleConfigRequestCompleted(object sender, RequestCompleteEventArgs<HPCGetConfig> e) 
    { 
     var request = e.Request; 
     // do something with the request 
    } 
} 

// the non-generic event args class 
public abstract class RequestCompleteEventArgs : EventArgs 
{ 
    public abstract Type RequestType { get; } 
    public abstract object RequestObject { get; set; } 
} 

// the generic event args class 
public class RequestCompleteEventArgs<T> : RequestCompleteEventArgs 
{ 
    private T m_Request; 


    public T Request 
    { 
     get { return m_Request; } 
     set { m_Request = value; } 
    } 

    public override Type RequestType 
    { 
     get { return typeof(T); } 
    } 

    public override object RequestObject 
    { 
     get { return Request; } 
     set 
     { 
      if (!(value is T)) 
      { 
       throw new ArgumentException("Invalid type.", "value"); 
      } 
      m_Request = (T)value; 
     } 
    } 
} 

// the non-generic interface 
public interface IHPCRequest 
{ 
    event EventHandler<RequestCompleteEventArgs> RequestComplete; 
} 

// the generic base class 
public abstract class HPCRequest<T> : IHPCRequest 
    where T : HPCRequest<T> 
{ 
    // this sanitizes the event handler, and makes it callable 
    // whenever an event handler is subscribed to the non-generic 
    // interface 
    private static EventHandler<RequestCompleteEventArgs<T>> ConvertNonGenericHandler(EventHandler<RequestCompleteEventArgs> handler) 
    { 
     return (sender, e) => handler(sender, e); 
    } 

    // this object is for a lock object for thread safety on the callback event 
    private readonly object Bolt = new object(); 

    // This determines whether the send method should raise the completed event. 
    // It is false by default, because otherwise you would have issues sending the request asynchronously 
    // without using the SendAsync method. 
    public bool AutoRaiseCompletedEvent { get; set; } 

    // This is used to ensure that RequestComplete event cannot fire more than once 
    public bool HasRequestCompleteFired { get; private set; } 

    // declare the generic event 
    public event EventHandler<RequestCompleteEventArgs<T>> RequestComplete; 

    // explicitly implement the non-generic interface by wiring the the non-generic 
    // event handler to the generic event handler 
    event EventHandler<RequestCompleteEventArgs> IHPCRequest.RequestComplete 
    { 
     add { RequestComplete += ConvertNonGenericHandler(value); } 
     remove { RequestComplete -= ConvertNonGenericHandler(value); } 
    } 

    // I'm not 100% clear on your intended functionality, but this will call an overrideable send method 
    // then raise the OnRequestCompleted event if the AutoRaiseCompletedEvent property is set to 'true' 
    public void Send() 
    { 
     SendRequest((T)this); 

     if(AutoRaiseCompletedEvent) 
     { 
      OnRequestCompleted((T)this); 
     } 
    } 

    public void SendAsync() 
    { 
     // this will make the event fire immediately after the SendRequest method is called 
     AutoRaiseCompletedEvent = true; 
     new Task(Send).Start(); 
    } 

    // you can make this virtual instead of abstract if you don't want to require that the Request 
    // class has the Send implementation 
    protected abstract void SendRequest(T request); 

    // this raises the RequestCompleted event if it is the first call to this method. 
    // Otherwise, an InvalidOperationException is thrown, because a Request can only 
    // be completed once 
    public void OnRequestCompleted(T request) 
    { 
     bool invalidCall = false; 
     Exception handlerException = null; 
     if (HasRequestCompleteFired) 
      invalidCall = true; 
     else 
     { 
      lock(Bolt) 
      { 
       if(HasRequestCompleteFired) 
       { 
        invalidCall = true; 
       } 
       else 
       { 
        if (RequestComplete != null) 
        { 
         // because you don't want to risk throwing an exception 
         // in a locked context 
         try 
         { 
          RequestComplete(this, new RequestCompleteEventArgs<T> { Request = request }); 
         } 
         catch(Exception e) 
         { 
          handlerException = e; 
         } 
        } 
        HasRequestCompleteFired = true; 
       } 
      } 
     } 
     if(invalidCall) 
     { 
      throw new InvalidOperationException("RequestCompleted can only fire once per request"); 
     } 
     if(handlerException != null) 
     { 
      throw new InvalidOperationException("The RequestComplete handler threw an exception."); 
     } 
    } 


} 

// a sample concrete implementation 
public class HPCGetConfig : HPCRequest<HPCGetConfig> 
{ 
    protected override void SendRequest(HPCGetConfig request) 
    { 
     // do some configuration stuff 
    } 
} 
+0

Nah,這或多或少具有我附加代碼已有的功能。我想添加的唯一功能是強類型事件參數。在您的示例中,您仍然必須將請求完成的參數轉換爲適當的請求類型。 – Alain

+0

@Alain,你錯了。 'RequestComplete'事件的簽名是'void HandleEvent(object sender,RequestCompleteEventArgs )',你可以通過'Request'屬性訪問「合適的請求類型」,你也可以通過'RequestObject'屬性。這兩個選項都可用於此實現 – smartcaveman