使用动态确定的类类型静态定义成员

时间:2011-11-02 19:45:45

标签: c# events .net-3.5 types

我正在尝试创建一个事件委托,其中参数的强类型与当前类匹配,如下所示:

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

6 个答案:

答案 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)

无法完成

我最终不得不在任何地方使用代理人。

如果有任何其他原因,请尽快更改已接受的答案。