c#泛型协变和逆变冲突

c# generics covariance and contravariance conflict

我使用接口、继承和泛型用 c# 编写了以下代码:

public interface IBasic
{

}

public class Basic : IBasic
{

}

public class AnotherBasic : Basic
{

}

public interface IWorker<in TBasic>
{
    void Run(TBasic basic);
}

public class Worker : IWorker<Basic>
{
    public void Run(Basic basic)
    {
        throw new System.NotImplementedException();
    }
}

public class AnotherWorker : IWorker<AnotherBasic>
{
    public void Run(AnotherBasic basic)
    {
        throw new System.NotImplementedException();
    }
}

public void Test()
{
    List<IWorker<IBasic>> workers = new List<IWorker<IBasic>>
    {
        new Worker(),
        new AnotherWorker()
    };
}

这段代码的问题是工人和另一个工人 classes 不适合 IWorker<IBasic> 的泛型列表,他们是工人和基本 class。问题是 IWorker<in TBasic> 是逆变的,因为 运行 方法签名,但是我需要它是协变的,以便填充 List<IWorker<IBasic>>。 运行 方法必须有 TBasic 参数,我需要这个工作人员列表是为了责任链设计模式。我是否遗漏了什么,或者我是否找到了使协变和逆变不互斥的理由?

你可以这样初始化它:

public void Test()
{
    List<IWorker<IBasic>> workers = new List<IWorker<IBasic>>
    {
        new Worker<IBasic>(),
        new AnotherWorker<IBasic>()
    };
    workers[0].Run(new Basic());
}

你的工人声明说 "this is a list of workers, and each one can run any implementation of IBasic",这是不正确的。

您可以尝试将 worker 可以处理的命令类型的责任转移到 worker 本身(事实上,这就是 chain of responsibility pattern 建议的)。

public interface IWorker
{
    bool DidRun<TBasic>(TBasic basic);
}

public class WorkerChain
{
    private readonly List<IWorker> workers = new List<IWorker>
    {
        new Worker(),
        new AnotherWorker()
    };

    public bool DidRun<T>(T basic)
    {
        return workers.Any(worker => worker.DidRun(basic));
    }
}

public class Worker : IWorker
{
    public bool DidRun<T>(T basic)
    {
        if (!(basic is Basic))
        {
            return false;
        }

        Console.WriteLine($"running {basic}");
        return true;
    }
}

public class Test
{
    public void CanRunWorkBasic()
    {
        var didRun = new WorkerChain().DidRun(new Basic());
        Debug.Assert(didRun);
    }
}

如果你想将你的工人插入到列表中,你将需要一个非通用接口 IWorker 并且 IWorker<TBasic> 必须实现该接口。然后在 List 中使用 IWorker 而不是 IWorker<TBasic>。现在将您的工人添加到 List 不再有问题。

这样我们解决了一个问题,但不幸的是我们还创建了另一个问题,因为我们必须执行两次Run方法。一次用于非通用接口,第二次用于通用接口。

您可以使用抽象 Worker class 来解决这个问题,默认情况下,当调用非泛型 Run 时,会进行必要的检查,转换参数并传递它通用 Run 方法。然后你的工人可以从 Worker 派生,每个人都可以有自己的角色。

在下面的示例中,我试图展示在我看来,这段代码应该是什么样子。实现了非泛型 Run 方法 explicitly and to be safe I also used generic type constraintsRun 方法只检查类型并将其进一步传递。

public interface IBasic
{

}

public class Basic : IBasic
{

}

public class AnotherBasic : Basic
{

}

public interface IWorker
{
    void Run(IBasic basic);
}

public interface IWorker<in TBasic> : IWorker where TBasic : IBasic
{
    void Run(TBasic basic);
}

public abstract class Worker<TBasic> : IWorker<TBasic> where TBasic : IBasic
{
    void IWorker.Run(IBasic basic)
    {
        if (basic is TBasic)
            Run((TBasic)basic);
    }

    public abstract void Run(TBasic basic);
}

public class FirstWorker : Worker<Basic>
{
    public override void Run(Basic basic)
    {
        // ...
    }
}

public class SecondWorker : Worker<AnotherBasic>
{
    public override void Run(AnotherBasic basic)
    {
        // ...
    }
}


public void Test()
{
    List<IWorker> workers = new List<IWorker>
    {
        new FirstWorker(),
        new SecondWorker()
    };
}

所以经过2天的学习和调查,我回答了我自己的问题。这是代码:

public 界面 IBasic {

}

public class Basic : IBasic
{

}

public class AnotherBasic : Basic
{

}

public interface IWorker<in TBasic>
{
    void Run(TBasic basic);
}

public class SimpleWorker : IWorker<IBasic>
{
    public void Run(IBasic basic)
    {
        throw new System.NotImplementedException();
    }
}

public class Worker : IWorker<Basic>
{
    public void Run(Basic basic)
    {
        throw new System.NotImplementedException();
    }
}

public class AnotherWorker : IWorker<AnotherBasic>
{
    public void Run(AnotherBasic basic)
    {
        throw new System.NotImplementedException();
    }
}

public class Final
{
    public void Test()
    {
        List<IWorker<AnotherBasic>> workers = new List<IWorker<AnotherBasic>>
        {
            new SimpleWorker(),
            new Worker(),
            new AnotherWorker()
        };
    }
}

CONTRAVARIANT中的TBasic,表示声明要尽可能具体,如代码所示:AnotherBasic 然后派生较少的类型,即父类,被接受,代码编译。