混合协方差和反方差

本文关键字:方差 混合 | 更新日期: 2023-09-27 18:04:34

目标:

在以下集合上迭代

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()
};

因为Generic参数不再是输出安全的。

问题:

有什么方法可以完成我的场景吗?

混合协方差和反方差

通过将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)方法的额外cruft,该方法在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事件中的正常模式(。