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 me
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:
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.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.
ou can make Catalog class generic. That will allow you to use GenericListItemCountChangedEvent T
public class Catalog<T> where T: BaseItem
{
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 EventHandler<GenericListItemCountChangedEventArgs<T>> GenericListItemCountChanged;
private void RaiseGenericListItemCountChangedEvent(List<T> List)
{
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;
}
}
}
public class MainWindow
{
public MainWindow()
{
new Catalog<BaseItem>().GenericListItemCountChanged += (sender, e) => GenericListItemCountChanged(sender, e);
}
private void GenericListItemCountChanged<T>(object sender, Catalog<BaseItem>.GenericListItemCountChangedEventArgs<T> e) where T : BaseItem
{
//Use Generic EventArgs
}
}