c#泛型协方差与逆变冲突

c#泛型协方差与逆变冲突,c#,generics,covariance,contravariance,C#,Generics,Covariance,Contravariance,我使用接口、继承和泛型用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> {

我使用接口、继承和泛型用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()
    };
}
公共接口IBasic
{
}
公共类基础:IBasic
{
}
公共类另一个基本类:基本类
{
}
公共接口IWorker
{
无效运行(TBasic basic);
}
公共类工人:IWorker
{
公开作废运行(基本)
{
抛出新系统。NotImplementedException();
}
}
公共类另一个工作人员:IWorker
{
公开作废运行(另一个基本)
{
抛出新系统。NotImplementedException();
}
}
公开无效测试()
{
列出工作人员=新列表
{
新工人(),
新的另一个工人()
};
}

此代码的问题是worker和其他worker类不适合
IWorker
的泛型列表,后者是worker和基本类的worker的父类。问题是,由于运行方法签名,
IWorker
是逆变的,但是我需要它是协变的,以便填写
列表。run方法必须有TBasic参数,我需要这个工作人员列表,用于责任链设计模式。我是否遗漏了一些东西,或者我是否找到了使协方差和逆变互不排斥的理由?

您可以这样初始化它:

public void Test()
{
    List<IWorker<IBasic>> workers = new List<IWorker<IBasic>>
    {
        new Worker<IBasic>(),
        new AnotherWorker<IBasic>()
    };
    workers[0].Run(new Basic());
}
公共无效测试()
{
列出工作人员=新列表
{
新工人(),
新的另一个工人()
};
辅助工[0]。运行(新建基本());
}

您可以这样初始化它:

public void Test()
{
    List<IWorker<IBasic>> workers = new List<IWorker<IBasic>>
    {
        new Worker<IBasic>(),
        new AnotherWorker<IBasic>()
    };
    workers[0].Run(new Basic());
}
公共无效测试()
{
列出工作人员=新列表
{
新工人(),
新的另一个工人()
};
辅助工[0]。运行(新建基本());
}

您的worker声明中说“这是一个worker列表,每个worker都可以运行IBasic的任何实现”,这是不正确的

您可以尝试将工作人员可以处理的命令类型的责任转移到工作人员本身(事实上,这就是报告所建议的)

公共接口IWorker
{
布尔·迪德伦(TBasic-basic);
}
公共级工作链
{
私有只读列表工作者=新列表
{
新工人(),
新的另一个工人()
};
公共图书馆(T基础)
{
返回workers.Any(worker=>worker.DidRun(basic));
}
}
公共类工人:IWorker
{
公共图书馆(T基础)
{
如果(!(基本即基本))
{
返回false;
}
WriteLine($“正在运行{basic}”);
返回true;
}
}
公开课考试
{
public void CanRunWorkBasic()
{
var didRun=newworkerchain().didRun(new Basic());
Assert(didRun);
}
}

您的worker声明中说“这是一个worker列表,每个worker都可以运行IBasic的任何实现”,这是不正确的

您可以尝试将工作人员可以处理的命令类型的责任转移到工作人员本身(事实上,这就是报告所建议的)

公共接口IWorker
{
布尔·迪德伦(TBasic-basic);
}
公共级工作链
{
私有只读列表工作者=新列表
{
新工人(),
新的另一个工人()
};
公共图书馆(T基础)
{
返回workers.Any(worker=>worker.DidRun(basic));
}
}
公共类工人:IWorker
{
公共图书馆(T基础)
{
如果(!(基本即基本))
{
返回false;
}
WriteLine($“正在运行{basic}”);
返回true;
}
}
公开课考试
{
public void CanRunWorkBasic()
{
var didRun=newworkerchain().didRun(new Basic());
Assert(didRun);
}
}

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

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

您可以使用一个抽象的
Worker
类来解决这个问题,默认情况下,当调用非泛型
Run
时,该类会进行必要的检查,强制转换参数并将其传递给泛型
Run
方法。然后,您的员工可以从
员工
中解脱出来,每个员工都可以有自己的角色

在下面的示例中,我试图展示在我看来,这段代码应该是什么样子的。实现了非通用的
Run
方法,为了安全起见,我还使用了该方法
Run
方法只是检查类型并进一步传递它

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()
    };
}
公共接口IBasic
{
}
公共类基础:IBasic
{
}
公共类另一个基本类:基本类
{
}
公共接口IWorker
{
无效运行(IBasic基本);
}
公共接口IWorker:IWorker,其中TBasic:IBasic
{
无效运行(TBasic basic);
}
公共抽象类工作者:IWorker,其中TBasic:IBasic
{
void IWorker.Run(IBasic)
{
if(基本为TBasic)
运行((TBasic)basic);
}
公开摘要无效运行(TBasic basic);
}
公共类第一工人:工人
{
公共覆盖无效运行(基本)
{
// ...
}
}
公共类第二工人:工人
{
公共覆盖无效运行(另一个基本)
{
// ...
}
}
公开无效测试()
{
列出工作人员=新列表
{
新的FirstWorker(),
新的第二工人()
};
}

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

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