Statically define a member using a dynamically defined class type

I am trying to create an event delegate where the parameter is strongly typed to match the current class, for example:

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

The problem is that the point of this class must be inherited, and I really want all these inheriting classes to have the "RequestComplete" event, where a delegate was introduced for this class:

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

This is because at the moment, when I have a function that processes one of my "RequestComplete" events, I currently have to do this:

  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. 

But I want to be able to do something like this:

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

Attempts

I tried this:

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

but when I try to trigger an event from the base class using RequestComplete(this); , I get a compile-time error: `Delegate 'RequestCompleteHandler' has some invalid arguments.

This will happen if I configured the entire HPCRequest class as HPCRequest<T> by going:

 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> { ... 

The same thing happens when you try to raise an event: RequestComplete(this);

I also tried all delegate creation forms and events and redefined them, for example:

 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; 

But this gives me a compile-time error because I cannot override the RequestComplete event with one of the delegate types.

Any other ideas?


Edit

The template for the entire HPCRequest class is not an option, after a very thorough attempt, I see that it just screwed up each attempt to use the HPCRequest type as a placeholder for any type of request. If this solution works, the HPCRequest class must be created and inherited without specifying a type parameter. I need a solution that does not require HPCRequest templates.

To make sure everyone knows exactly how I am trying to use this, I inserted some code examples in pastebin so you can experiment with how to get this event to pattern work without breaking anything. Here it is: http://pastebin.com/bbEYgLj1

+4
source share
6 answers

This is impossible to do .

I had to drop the delegate wherever it was used.

Happy to change the accepted answer, if anything else shows otherwise.

0
source

What could you try:

 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 a Foo! } } 

This self-referential general restriction allows what you want, I think. I added protected RaiseRequestCompleted so you can still raise an event from classes that inherit from HCPRequest. Otherwise, only HCPRequest .

UPDATE : I updated the code for passing this and added an example code that matches your desired result.

+3
source

You can use the generic type parameter for these purposes, so that each class inherited from HPCRequest indicates a parameter of the general type. I would also suggest labeling classes that were designed for the base class using the abstract modifier to avoid creating an instance explicitly:

 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> { } 
+2
source

You can restrict the type parameter of an abstract class to be inferred from the abstract class itself. It seems strange, but you can configure it like this:

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

Your use case is an excellent candidate for , clearly implementing a non-common interface in the base base class . I'm not sure I fully understand the desired functionality, but I think I did. I wrote code (below) that should run you.

As a side note, there is no true reason for declaring delegates. To meet MSDN Event Design standards, your solution will use EventHandler<T> and a custom implementation of EventArgs .

Code

 // 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 } } 
+1
source

I think that you will need to create an abstract base class that cannot be created:

 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 } 

Also 'this' should be sent to T: OnRequestComplete((T)this);

This test runs without errors:

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

Source: https://habr.com/ru/post/1379375/


All Articles