混合协变性和逆变性

3

目标:

遍历以下集合

var collection = new IImportTrigger<EventArgs>[]
{
    new FileSystemImportTrigger()
    , new TimerImportTrigger()
};

以这种方式
foreach (var trigger in collection)
{
    trigger.Import += trigger.OnImport;
}

这是我目前为止的内容

public delegate void ImportTriggerEventHandler<in T>(object sender, T args) where T : EventArgs;

public interface IImportTrigger<out T> where T : EventArgs
{
    event ImportTriggerEventHandler<T> Import;
    void OnImport<T1>(object sender, T1 args) where T1 : EventArgs;
}

public class FileSystemImportTrigger : IImportTrigger<FileSystemEventArgs>
{
    public event ImportTriggerEventHandler<FileSystemEventArgs> Import;

    public void OnImport<T>(object sender, T args) where T : EventArgs {  }
}

public class TimerImportTrigger : IImportTrigger<ElapsedEventArgs>
{
    public event ImportTriggerEventHandler<ElapsedEventArgs> Import;

    public void OnImport<T>(object sender, T args) where T : EventArgs {  }
}

期望:

我希望只使用一个泛型参数来定义IImportTrigger。

问题:

如果我将接口定义更改为以下内容(注意,泛型参数T不再是协变的)。

public interface IImportTrigger<T> where T : EventArgs
{
    event ImportTriggerEventHandler<T> Import;
    void OnImport(object sender, T args);
}

因此

public class FileSystemImportTrigger : IImportTrigger<FileSystemEventArgs>
{
    public event ImportTriggerEventHandler<FileSystemEventArgs> Import;

    public void OnImport(object sender, FileSystemEventArgs args) { }
}

public class TimerImportTrigger : IImportTrigger<ElapsedEventArgs>
{

    public event ImportTriggerEventHandler<ElapsedEventArgs> Import;

    public void OnImport(object sender, ElapsedEventArgs args) { }
}

我无法为我的集合创建通用类型

var collection = new IImportTrigger<EventArgs>[]
{
    new FileSystemImportTrigger()
    , new TimerImportTrigger()
};

因为泛型参数不再是输出安全的。 问题: 有没有办法实现我的场景?
1个回答

2

通过将OnImport切换为完全不通用,然后使用显式接口,再创建另一个更具体的接口,该接口不是协变的,其中包含了通用版本的OnImport,你可以做到这一点。

internal class Program
{
    private static void Main(string[] args)
    {
        var collection = new IImportTriggerBase<EventArgs>[]
        {
            new FileSystemImportTrigger()
            , new TimerImportTrigger()
        };

        foreach (var trigger in collection)
        {
            trigger.Import += trigger.OnImport;
        }
    }
}

public delegate void ImportTriggerEventHandler<in T>(object sender, T args) where T : EventArgs;

public interface IImportTriggerBase<out T> where T : EventArgs
{
    event ImportTriggerEventHandler<T> Import;
    void OnImport(object sender, EventArgs args);
}

public interface IImportTrigger<T> : IImportTriggerBase<T> where T : EventArgs
{
    void OnImport(object sender, T args);
}

public class FileSystemImportTrigger : IImportTrigger<FileSystemEventArgs>
{
    public event ImportTriggerEventHandler<FileSystemEventArgs> Import;

    public void OnImport(object sender, FileSystemEventArgs args) { }

    void IImportTriggerBase<FileSystemEventArgs>.OnImport(object sender, EventArgs args)
    {
        OnImport(sender, (FileSystemEventArgs)args);
    }
}

public class TimerImportTrigger : IImportTrigger<ElapsedEventArgs>
{
    public event ImportTriggerEventHandler<ElapsedEventArgs> Import;

    public void OnImport(object sender, ElapsedEventArgs args) { }

    void IImportTriggerBase<ElapsedEventArgs>.OnImport(object sender, EventArgs args)
    {
        OnImport(sender, (ElapsedEventArgs)args);
    }
}

然而,这会给你额外的OnImport(object sender, EventArgs args)方法的负担,它在IImportTrigger<T>上是可见的。


如果我理解正确,你只是想让派生类能够注意到Import正在被触发,实际上并不需要暴露OnImport,我会这样做:

internal class Program
{
    private static void Main(string[] args)
    {
        var collection = new IImportTrigger<EventArgs>[]
        {
            new FileSystemImportTrigger()
            , new TimerImportTrigger()
        };
    }
}

public delegate void ImportTriggerEventHandler<in T>(object sender, T args) where T : EventArgs;

public interface IImportTrigger<out T> where T : EventArgs
{
    event ImportTriggerEventHandler<T> Import;
}

public abstract class OnImportBase<T> : IImportTrigger<T> where T: EventArgs
{

    public event ImportTriggerEventHandler<T> Import;

    protected virtual void OnImport(object sender, T args)
    {
        var tmp = Import;
        if (tmp != null)
        {
            tmp(this, args);
        }
    }
}

public class FileSystemImportTrigger : OnImportBase<FileSystemEventArgs>
{
    protected override void OnImport(object sender, FileSystemEventArgs args)
    {
        DoSomeExtraStuffBeforeImport();
        base.OnImport(sender, args);
    }

    private void DoSomeExtraStuffBeforeImport()
    {
    }
}

public class TimerImportTrigger : OnImportBase<ElapsedEventArgs>
{
    protected override void OnImport(object sender, ElapsedEventArgs args)
    {
        base.OnImport(sender, args);
        DoSomeExtraStuffAfterImport();
    }

    private void DoSomeExtraStuffAfterImport()
    {
    }
}

这样可以取消事件订阅,将其作为重载处理(这是.NET事件中的正常模式)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接