C# Windows窗体中通用事件处理程序的变通方法

C# Windows窗体中通用事件处理程序的变通方法,c#,winforms,generics,visual-studio-2013,event-handling,C#,Winforms,Generics,Visual Studio 2013,Event Handling,很久以前,我注意到VisualStudio的Windows窗体编辑器不支持包含泛型类型参数的事件。例如,像这样的事件 public event EventHandler<ListEventArgs<int>> MyStrangeEvent { add { ... } remove { ... } } public事件事件处理程序MyStrangeEvent{add{…}remove{…} 在哪里 公共类ListEventTargets:EventArgs{List a

很久以前,我注意到VisualStudio的Windows窗体编辑器不支持包含泛型类型参数的事件。例如,像这样的事件

public event EventHandler<ListEventArgs<int>> MyStrangeEvent { add { ... } remove { ... } }
public事件事件处理程序MyStrangeEvent{add{…}remove{…}
在哪里

公共类ListEventTargets:EventArgs{List args;}
甚至不会显示在Visual Studio的属性管理器的事件列表中。现在,这是一个有点人为的示例,可以通过重写类及其事件轻松地修改它以在VisualStudio中工作。但是,我目前正在进行一个项目,由于兼容性原因,我无法更改某些类。我唯一能做的就是更改用户控件的事件。此控件的事件当前如下所示:

public event EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
public class Plane<T> where T : ISurface
{
    ...
    public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
    ...
    public class DrawingErrorEventArgs : EventArgs { ... /* Uses T */ ... }
}
public interface IDataArgs<out T>
{
    T Data { get; }
}

public class DataEventArgs<T> : EventArgs, IDataArgs<T>
{
    public DataEventArgs<T>(T data) 
    {
        _data = data;
    }
    private T _data;
    public T Data { get { return _data; } }
}
public event EventHandler drawingerro{add{{u Plane.drawingerro+=value;}remove{{u Plane.drawingerro-=value;}
请注意,不能更改基础平面类(由受保护字段的_Plane实例表示)。其DrawingError事件及其EventArgs类型在Plane类中声明如下:

public event EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
public class Plane<T> where T : ISurface
{
    ...
    public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
    ...
    public class DrawingErrorEventArgs : EventArgs { ... /* Uses T */ ... }
}
public interface IDataArgs<out T>
{
    T Data { get; }
}

public class DataEventArgs<T> : EventArgs, IDataArgs<T>
{
    public DataEventArgs<T>(T data) 
    {
        _data = data;
    }
    private T _data;
    public T Data { get { return _data; } }
}
公共类平面,其中T:i曲面
{
...
公共事件EventHandler DrawingError=null;
...
公共类DrawingErrorEventArgs:EventArgs{…/*使用T*/…}
}
当然,VisualStudio的Windows窗体编辑器不会显示我的用户控件的任何事件。我一直在寻找一些解决办法,以使他们再次显示,但没有能够找到一个解决办法,实际工作。以下是我尝试过的一些事情:

  • 创建了一个MyPlane类,该类继承自Plane并使用它:
    公共事件事件处理程序DrawingError…
    。由于我不知道的原因,这些事件仍然没有出现在编辑器中。这可能是由于事件的参数,其中一些参数仍然是通用的。在下面找到一个简单的工作示例
  • 创建了一个helper类,该类定义了
    EventHandler
    EventHandler
    之间的隐式转换运算符,其中GDIPlane只是一个从
    Plane
    继承的伪类。这在某种程度上确实有效,但会复制事件调用,因为转换会创建新的事件处理程序,这些事件处理程序会传递到无法正确删除/注销的_平面
  • 试图从
    EventHandler
    继承,这显然不起作用,因为
    EventHandler
    是密封的
  • 是否有其他方法使我的事件在Windows窗体编辑器中再次可见

    致意 安德烈亚斯

    编辑:1的最小工作示例:

    public interface ISurface { }
    
    public class GDISurface : ISurface { }
    
    public class Plane<T> where T : ISurface
    {
        public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
        public class DrawingErrorEventArgs : EventArgs { T stuff; }
    }
    
    public class TestControl : UserControl
    {
        public class GDIPlane : Plane<GDISurface>  { }
        GDIPlane _Plane = null;
        public event EventHandler<GDIPlane.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
    }
    
    公共接口ISurface{}
    公共类GDISurface:ISurface{}
    公共类平面,其中T:i曲面
    {
    公共事件EventHandler DrawingError=null;
    公共类DrawingErrorEventArgs:EventArgs{T stuff;}
    }
    公共类TestControl:UserControl
    {
    公共类GDIPlane:平面{}
    GDIPlane _平面=空;
    公共事件事件处理程序DrawingError{add{{U Plane.DrawingError+=value;}remove{{U Plane.DrawingError-=value;}}
    }
    
    单击TestControl实例时,属性管理器中的事件列表中不会显示DrawingError

    EDIT2:这是原始问题(没有任何解决方法),TestControl的DrawingError事件不会出现:

    public interface ISurface { }
    
    public class GDISurface : ISurface { }
    
    public class Plane<T> where T : ISurface
    {
        public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
        public class DrawingErrorEventArgs : EventArgs { T stuff; }
    }
    
    public class TestControl : UserControl
    {
        Plane<GDISurface> _Plane = null;
        public event EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
    }
    
    公共接口ISurface{}
    公共类GDISurface:ISurface{}
    公共类平面,其中T:i曲面
    {
    公共事件EventHandler DrawingError=null;
    公共类DrawingErrorEventArgs:EventArgs{T stuff;}
    }
    公共类TestControl:UserControl
    {
    平面_平面=零;
    公共事件事件处理程序DrawingError{add{{U Plane.DrawingError+=value;}remove{{U Plane.DrawingError-=value;}}
    }
    
    这是Visual Studio特有的行为,其原因在于EventHandler没有在其“TEventArgs”上指定协方差(这可能会施加看似愚蠢的限制),并且这些工具没有对代码执行足够的内省,无法生成适当的类型(即使您在构造控件中留下了类型数据的踪迹),因此,看起来VS不支持通用事件属性。您可以考虑提交一个特征请求,但我不建议将其归档为bug,因为它们可以将其标记为“设计”并关闭它。 一般来说,如果您需要事件的通用类型参数,并且需要它们的设计时支持(这是不同的实现关注点),那么您可以考虑将它们包装到特定于表示的外观中(例如,“额外的代码层以满足设计时需要”)

    就我个人而言,我会减少你现在使用的泛型类型,这似乎有点过分,如果你不理解泛型类型中的协方差/逆变,可能会在某个时候让你陷入困境,比如现在

    但是,要解决您的问题:

    考虑使用自定义事件args类,该类可以传输非泛型属性中的数据,还可以使用非泛型
    EventHandler
    event/property。然后,了解事件的“类型”将从泛型类型参数转移到非泛型事件args,而由非泛型事件args负责。如果t args不够,您可以添加一个属性来传递事件类型(或数据类型),以便接收代码能够正确地解释它(当然,假设它还没有通过其他方式知道):

    这通常仅用于通过事件链传递数据,通常实现如下:

    public class DataEventArgs<T> : EventArgs
    {
        public T Data { get; set; }
    }
    
    为了补充上述内容,以下是非通用事件处理程序的外观:

    private void testControl1_DrawingError(object sender, EventArgs e)
    {
        var genericDrawingErrorEventArgs = e as Plane<GDISurface>.DrawingErrorEventArgs;
        if (genericDrawingErrorEventArgs != null)
        {
            // TODO:
        }
    }
    
    private void testControl1\u DrawingError(对象发送方,事件参数e)
    {
    var genericDrawingErrorEventArgs=e作为平面。DrawingErrorEventArgs;
    如果(通用)