Noel Widmer
Noel Widmer

Reputation: 4572

Passing a generic List<T> as a delegate parameter

I wrote an event which is supposed to pass a List with generic items of type (T) to all subscribers:

public delegate void GenericListItemCountChangedEvent(List<Object> sender, GenericListItemCountChangedEventArgs e);
public event GenericListItemCountChangedEvent GenericListItemCountChanged;

private void RaiseGenericListItemCountChanged<T>(List<T> List) where T : BaseElem {
    if (GenericListItemCountChanged != null)
        GenericListItemCountChanged(List as List<Object>, new GenericListItemCountChangedEventArgs(typeof(T)));
}

This is a statement from a class, which subscribes to the event above:

 _catalog.GenericListItemCountChanged += (sender, e) => GenericListItemCountChanged(sender, e);

And this is the method the event is forwarded to inside the subscriber:

private void GenericListItemCountChanged(List<Object> sender, Catalog.GenericListItemCountChangedEventArgs e) {
    Catalog.ListTypes changedListType = Catalog.GetListTypeFromList(changedList); 
    //GetListTypeFromList expects parameter to be a type of <T> where T : BaseElem
}

Obviously, a List<Object> cannot be converted to a List<T> where T : BaseElem.

I'll accept your answer if you could provide a solution for retrieving the List with items of their "real" type inside the method, which handles the event.
(Restoring original List state)

The "real" type of the items inside the list is stored inside the EventArgs.
But I don't know if that could help me...

SOLUTION:
I could not have figured this out without the help of @ChrFin, thanks mate!

public class Catalog : Object

public delegate void GenericListItemCountChangedEvent(IEnumerable<BaseElem> sender, GenericListItemCountChangedEventArgs e);
public event GenericListItemCountChangedEvent GenericListItemCountChanged;

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

public class GenericListItemCountChangedEventArgs {
    public GenericListItemCountChangedEventArgs() {
    }
}
    public static ListTypes GetListTypeFromList<T>(IEnumerable<T> List) where T : BaseElem {
        Type typeofT = List.GetType().GetGenericArguments()[0];
        //typeofT now contains correct subtype of 'BaseElem'
        return ListTypes.SomeType //based on 'typeOfT'
    }

public partial class MainWindow : Window

    _catalog.GenericListItemCountChanged += (sender, e) => GenericListItemCountChanged(sender, e);
private void GenericListItemCountChanged(IEnumerable<BaseElem> sender, Catalog.GenericListItemCountChangedEventArgs e) {
            Catalog.ListTypes changedListType = Catalog.GetListTypeFromList(sender);
            //We now know the changed ListType
}

Thank you very much!

Upvotes: 1

Views: 1488

Answers (1)

Christoph Fink
Christoph Fink

Reputation: 23113

There are two (three) ways to solve this: Covariance and generics:

1) Generics:
You also need to create a generic delegate:

public class YourClass<T> where T : BaseElem
{
    public delegate void GenericListItemCountChangedEvent<T>(List<T> sender,
                             GenericListItemCountChangedEventArgs e);
    public event GenericListItemCountChangedEvent<T> GenericListItemCountChanged;

    private void RaiseGenericListItemCountChanged<T>(List<T> List)
    {
        if (GenericListItemCountChanged != null)
        {
            GenericListItemCountChanged(List,
               new GenericListItemCountChangedEventArgs(typeof(T)));
        }
    }
}

Of course you can then only use one explicit type of base type BaseElem in this case.

2) Covariance:
Using the IEnumerable<T> interface instead of the explicit type to support covariance:

public delegate void GenericListItemCountChangedEvent(IEnumerable<BaseElem> sender,
                         GenericListItemCountChangedEventArgs e);
public event GenericListItemCountChangedEvent GenericListItemCountChanged;

private void RaiseGenericListItemCountChanged(IEnumerable<BaseElem> List)
{
    if (GenericListItemCountChanged != null)
    {
        var itemType = List.Count() > 0 ? List.First().GetType() : typeof(BaseElem);
        GenericListItemCountChanged(List,
            new GenericListItemCountChangedEventArgs(itemType));
    }
}

3) Ugly, but would work: Casting:

public delegate void GenericListItemCountChangedEvent(List<BaseElem> sender,
                         GenericListItemCountChangedEventArgs e);
public event GenericListItemCountChangedEvent GenericListItemCountChanged;

private void RaiseGenericListItemCountChanged<T>(List<T> List) where T : BaseElem
{
    if (GenericListItemCountChanged != null)
    {
        GenericListItemCountChanged(List.Cast<BaseElem>().ToList(),
            new GenericListItemCountChangedEventArgs(typeof(T)));
    }
}

Upvotes: 1

Related Questions