简体   繁体   中英

Is it possible to implement a C# Interface and extend the arguments of Events?

Suppose I have the following Interface:

interface IBook
{
    event EventHandler<EventArgs> PageChanged;
}

which I could implement without difficulty for this class:

class Novel : IBook
{
    public event EventHandler<EventArgs> PageChanged;

    protected void OnPageChanged()
    {
        EventHandler<EventArgs> pageChanged = PageChanged;
        if (pageChanged != null) pageChanged(this, EventArgs.Empty);
    }
}

however, if I now have an object called Encyclopedia defined as:

class Encyclopedia : IBook
{
    public class EncyclopediaEventArgs : EventArgs
    {
        public int Volume
        {
            get { return volume; }
        }

        private int volume;

        public EncyclopediaEventArgs(int volume)
        {
            this.volume = volume;
        }
    }

    public event EventHandler<EncyclopediaEventArgs> PageChanged;

    protected void OnPageChanged(int volume)
    {
        EventHandler<EncyclopediaEventArgs> pageChanged = PageChanged;
        if (pageChanged != null) pageChanged(this, new EncyclopediaEventArgs(volume));
    }
}

which has all the workings of a Book , but with the added event argument field of Volume . When I compile, I get an error (as surmised I would):

error CS0738: 'Encyclopedia' does not implement interface member 'IBook.PageChanged'. 'Encyclopedia.PageChanged' cannot implement 'IBook.PageChanged' because it does not have the matching return type of 'System.EventHandler'

It states that it cannot implement IBook.PageChanged because System.EventHandler<System.EventArgs> is not the return type, even though EncyclopediaEventArgs derives from System.EventArgs .

My question, therefore, is, would it be possible to derive such a class as Encyclopedia which adds the additional Volume field to its event arguments?

(Any discussion is very much welcome as to why this is or is not a poor design/architecture decision!)

It seems fairly straight forward to do this:

interface IBook<T> where T : EventArgs
{
    event EventHandler<T> PageChanged;
}

class Novel : IBook<EventArgs> { ... }

class Encyclopedia : IBook<Encyclopedia.EncyclopediaEventArgs> { ... }

If you still need a plain IBook then you would do this:

interface IBook { }

interface IBook<T> : IBook where T : EventArgs
{
    event EventHandler<T> PageChanged;
}

It depends a bit on how you are going to use IBook . You could create a generic parameter for the EventArgs like so:

public interface IBook<TEventArgs> where TEventArgs : EventArgs
{
    event EventHandler<TEventArgs> PageChanged;
}

public class Novel : IBook<EventArgs>
{
    event EventHandler<EventArgs> PageChanged;
}

public class Encyclopedia : IBook<EncyclopediaEventArgs>
{
    event EventHandler<EncyclopediaEventArgs> PageChanged;
}

But then you can't use IBook without the generic type if you need PageChanged for other purposes.

Another way is to just keep the event EventHandler<EventArgs> PageChanged; have the Encyclopedia implementation pass a EncyclopediaEventArgs and just cast in the event handler.

class Encyclopedia : IBook
{
    public class EncyclopediaEventArgs : EventArgs
    {
    }

    public event EventHandler<EventArgs> PageChanged;

    protected void OnPageChanged(int volume)
    {
        EventHandler<EventArgs> pageChanged = PageChanged;
        if (pageChanged != null) pageChanged(this, new EncyclopediaEventArgs(...));
    }
}

public class BookReader
{
    public void OnPageChanged(object sender, EventArgs e)
    {
        if (sender is Encyclopedia && e is EncyclopediaEventArgs)
        {
            EncyclopediaEventArgs ee = (EncyclopediaEventArgs)e;
        }
        else
        {
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM