混合协方差和逆变

时间:2015-07-23 21:12:59

标签: c# generics

目标:

迭代以下集合

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参数不再是输出安全的。

问题:

有没有办法完成我的方案?

1 个答案:

答案 0 :(得分: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事件中的正常模式)。