C#将通用类作为事件参数传递 [英] C# Passing generic class as an event parameter

查看:61
本文介绍了C#将通用类作为事件参数传递的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近几天我一直在使用泛型,但是在尝试将泛型作为参数传递给事件/代理时遇到了一个问题.

一个非常友好的堆栈成员可以使我对上周五发布的仿制药的一个问题有所了解.但是,我想再次提出来,因为我仍然不知道如何使它正确工作.

在下面的代码中,您可以看到我有一个"Catalog"类,该类可以引发一个包含通用参数的事件.

"MainWindow"类预订此事件,并应与通用参数一起使用.

这不起作用,因为该事件必须指定委托的通用类型.

 公共类目录{#region活动公共委托无效GenericListItemCountChangedEvent< T(对象发送者,GenericListItemCountChangedEventArgs< e),其中T:BaseItem;//这是行不通的地方,因为我将BaseItem指定为类型公共事件GenericListItemCountChangedEvent< BaseItem>GenericListItemCountChanged;私有void RaiseGenericListItemCountChangedEvent< T>(List< T> List)其中T:BaseItem {如果(GenericListItemCountChanged!= null){GenericListItemCountChanged(this,new GenericListItemCountChangedEventArgs< T>(List));}}公共类GenericListItemCountChangedEventArgs< T>:EventArgs,其中T:BaseItem {私有列表< T>_changedList_propStore;公共列表< T>ChangedList {得到 {返回_changedList_propStore;}}公共GenericListItemCountChangedEventArgs(List< T> ChangedList){_changedList_propStore = ChangedList;}}#endregion活动}公共局部类MainWindow:Window {公共MainWindow(){_catalog.GenericListItemCountChanged + =(发送方,e)=>GenericListItemCountChanged(sender,e);}私有无效的GenericListItemCountChanged< T(对象发送者,Catalog.GenericListItemCountChangedEventArgs< T> e)其中T:BaseItem {//使用通用EventArgs}} 

有没有办法在"MainWindow"类中接收通用参数?还是我需要实施一些解决方法?

解决方案

根据您的确切需求,您可能可以保留方法的通用方面.但是我承认,为什么对我来说还不清楚.该方法绑定到特定的委托类型,即该方法专门引发的事件使用的类型.

使它具有通用性有什么价值?您是否真的要通过各种不同的列表(每个元素都有不同的类型)来引发这一事件?如果是这样,事件处理程序应该如何知道要为其提出事件的列表?我的意思是,可以通过反射来完成,但这并不是一个好的设计.

无论如何,如果您真的想编译代码,则需要对所涉及的类施加一些限制,并在委托类型声明中使用接口而不是实际的类.

例如:

 接口IBaseItemEventArgs< out T>{IReadOnlyList< T>ChangedList {get;}}类GenericListItemCountChangedEventArgs< T>:EventArgs,IBaseItemEventArgs< T>其中T:BaseItem{私有IReadOnlyList< T>_changedList_propStore;公共IReadOnlyList< T>变更清单{得到{返回_changedList_propStore;}}公共GenericListItemCountChangedEventArgs(List< T> ChangedList){_changedList_propStore = ChangedList.AsReadOnly();}}公众代表无效T中的GenericListItemCountChangedEvent<(对象发送者,IBaseItemEventArgs< T> e)其中T:BaseItem;公共静态事件GenericListItemCountChangedEvent< BaseItem>GenericListItemCountChanged;私有静态无效RaiseGenericListItemCountChangedEvent< T>(列表< T>列表)其中T:BaseItem{GenericListItemCountChangedEvent< T>处理程序= GenericListItemCountChanged;如果(处理程序!= null){handler(null,new GenericListItemCountChangedEventArgs< T>(List));}} 

这声明了接口 IBaseItemEventArgs< out T> ,即 T 是协变的(即返回的实际类型可能比使用接口),并且事件委托类型为 GenericListItemCountChangedEvent< in T> ,即 T 是互变的(即,分配给方法的处理程序的委托的类型可以具有更少的参数)派生,而不是该本地处理程序变量的声明中所使用的内容.

这两个共同允许:

  1. 用于从事件委托类型隐式转换的 RaiseGenericListItemCountChangedEvent< T>()方法,该事件委托类型的声明类型小于该方法中的 T 声明,该方法实际处理的处理程序类型.
  2. IBaseItemEventArgs< out T> 接口具有一个返回相似协变接口对象 IReadOnlyList< T> 的方法.

当然,如果处理程序需要能够修改列表,则代码将无法编译(不能使用 IReadOnlyList< T> ).但这是一件好事.如果呼叫者能够使用比声明的派生的列表元素类型更多的派生列表订阅事件,并且仍然允许更改列表,则可以将错误类型的元素添加到列表中.那根本不好.:)因此,语言规则可防止您犯该错误.

I've been working with generics over the last few days and I encountered an issue by trying to pass generic types as parameters in events/delegates.

A very friendly member of stack could bring some light into one question about generics I posted last Friday. However, I'd like to bring this up again, because I still don't understand how I am supposed to make it work correctly.

In the following code you can see that I have a class "Catalog", which can raise an event, which contains a generic parameter.

The class "MainWindow" subscribes to this event and should work with the generic parameter.

This doesn't work, because the event must specify the generic type of the delegate.

public class Catalog {

#region EVENTS

public delegate void GenericListItemCountChangedEvent<T>(object sender, GenericListItemCountChangedEventArgs<T> e) where T : BaseItem; 
//This is the point where it does not work, because I specify BaseItem as the type
public event GenericListItemCountChangedEvent<BaseItem> GenericListItemCountChanged;

private void RaiseGenericListItemCountChangedEvent<T>(List<T> List) where T : BaseItem {
    if (GenericListItemCountChanged != null) {
        GenericListItemCountChanged(this, new GenericListItemCountChangedEventArgs<T>(List));
    }
}

public class GenericListItemCountChangedEventArgs<T> : EventArgs where T : BaseItem {
    private List<T> _changedList_propStore;
    public List<T> ChangedList {
        get {
            return _changedList_propStore;
        }
    }

    public GenericListItemCountChangedEventArgs(List<T> ChangedList){
        _changedList_propStore = ChangedList;
    }
}

#endregion EVENTS

}


public partial class MainWindow : Window{

   public MainWindow(){
      _catalog.GenericListItemCountChanged += (sender, e) => GenericListItemCountChanged(sender, e);
   }

   private void GenericListItemCountChanged<T>(object sender, Catalog.GenericListItemCountChangedEventArgs<T> e) where T : BaseItem {
       //Use Generic EventArgs
   }

}

Is there a way to receive the generic parameter in the class "MainWindow"? Or do I need to implement some work-around?

解决方案

Depending on your exact needs, you might be able to preserve the generic aspect of your method. But I admit, it's not clear to me why you would want to do that. The method is tied to a specific delegate type, i.e. the type used by the event that method specifically raises.

Of what value would there be in having it generic at all? Are you really going to raise this one event with various different lists, each having different element types? If so, how is the event handler supposed to know what list the event is being raised for? I mean, it can be done via reflection, but that's hardly good design.

In any case, if you really want to get your code to compile, you will need to impose some restrictions on the classes involved, and use an interface instead of an actual class in the delegate type declaration.

For example:

interface IBaseItemEventArgs<out T>
{
    IReadOnlyList<T> ChangedList { get; }
}

class GenericListItemCountChangedEventArgs<T> : EventArgs, IBaseItemEventArgs<T>
    where T : BaseItem
{
    private IReadOnlyList<T> _changedList_propStore;
    public IReadOnlyList<T> ChangedList
    {
        get
        {
            return _changedList_propStore;
        }
    }

    public GenericListItemCountChangedEventArgs(List<T> ChangedList)
    {
        _changedList_propStore = ChangedList.AsReadOnly();
    }
}

public delegate void
    GenericListItemCountChangedEvent<in T>(object sender, IBaseItemEventArgs<T> e)
    where T : BaseItem;

public static event
    GenericListItemCountChangedEvent<BaseItem> GenericListItemCountChanged;

private static void RaiseGenericListItemCountChangedEvent<T>(List<T> List)
    where T : BaseItem
{
    GenericListItemCountChangedEvent<T> handler = GenericListItemCountChanged;

    if (handler != null)
    {
        handler(null, new GenericListItemCountChangedEventArgs<T>(List));
    }
}

This declares the interface IBaseItemEventArgs<out T>, i.e. with T being covariant (i.e. the actual type returned may be more derived than that used in the declaration that uses the interface), and the event delegate type GenericListItemCountChangedEvent<in T>, i.e. with T being contravariant (i.e. the type of the delegate assigned to the method's handler may have arguments less derived than that used in the declaration of that local handler variable).

These two together allow:

  1. The RaiseGenericListItemCountChangedEvent<T>() method to implicitly convert from the event delegate type, which has a declared type that is less-derived than the T in the method declaration, to the handler type the method actually deals with.
  2. The IBaseItemEventArgs<out T> interface to have a method which returns a similarly covariant interface object, IReadOnlyList<T>.

Of course, if the handler needs to be able to modify the list, then the code won't compile (can't use IReadOnlyList<T>). But that's a good thing. If a caller was able to subscribe to the event with a list element type more derived than that declared and still be permitted to change the list, it could add elements of the wrong type to the list. That wouldn't be good at all. :) So the language rules prevent you from making that mistake.

这篇关于C#将通用类作为事件参数传递的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆