C# 泛型协方差和逆变冲突



我有以下代码用 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()
};
}

此代码的问题在于 worker 和另一个 worker 类不适合作为worker 和基本类的 worker 父级的IWorker<IBasic>的泛型列表。问题是IWorker<in TBasic>由于运行方法签名而逆变,但是我需要协变才能填充List<IWorker<IBasic>>。run 方法必须具有 TBasic 参数,我需要这个责任链设计模式的工作人员列表。我是否错过了什么,或者我找到了使协方差和逆变不相互排斥的理由?

你可以像这样初始化它:

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

你的工人声明说"这是一个工人列表,每个人都可以运行IBasic的任何实现",这是不正确的。

你可以尝试将工作线程可以处理的命令类型的责任转移到工作线程本身(事实上,这就是责任链模式所暗示的)。

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类来解决此问题,默认情况下,当调用非泛型Run时,该类会进行必要的检查,强制转换参数并将其传递给泛型Run方法。然后,您的工作人员可以从Worker派生,每个人都可以有自己的角色。

在下面的示例中,我试图展示在我看来,这段代码应该是什么样子的。非泛型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 {

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

在逆变中,这意味着声明应该尽可能具体,如代码所示:另一个基本 然后,派生较少的类型(父级)被接受,并编译代码。

相关内容

  • 没有找到相关文章

最新更新