我正在尝试创建一个事件委托,其中参数的强类型与当前类匹配,如下所示:
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);
这是因为目前,当我有一个处理我的“RequestComplete”事件的函数时,我目前必须这样做:
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);
从基类中调用事件时,我得到一个编译时错误:`Delegate'RequestCompleteHandler'有一些无效的参数。
无论我是否将整个HPCRequest
课程设为HPCRequest<T>
,都会发生这种情况:
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> { ...
当我尝试调用事件时发生同样的错误:RequestComplete(this);
我还尝试了所有形式的创建委托和事件并覆盖它们,例如:
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
的解决方案。
为了确保每个人都确切知道我是如何使用它的,我将一些示例代码粘贴到pastebin中,这样可以让您尝试使用此方法模板化工作而不会破坏任何内容。这是:http://pastebin.com/bbEYgLj1
答案 0 :(得分: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
这样做。
更新:我更新了代码以传递this
并添加了与您所需结果相符的示例代码。
答案 1 :(得分: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>
{
}
答案 2 :(得分: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>
{
}
答案 3 :(得分:1)
您的用例非常适合在通用基类中显式实现非泛型接口。我不确定我是否完全理解所需的功能,但我想我做到了。我写了一些代码(下面),可以帮助你入门。
作为旁注,没有真正的理由申报代表。为了与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
}
}
答案 4 :(得分: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
}
同样'this'必须投放到T:OnRequestComplete((T)this);
此测试运行时没有错误:
var hpcr = new HPCRequest();
hpcr.Test();
hpcr.Test2();
答案 5 :(得分:0)
无法完成。
我最终不得不在任何地方使用代理人。
如果有任何其他原因,请尽快更改已接受的答案。