考虑这个简化的类层次结构:
public interface IProcessor
{
Task<TProcessor> Run<TProcessor>() where TProcessor : IProcessor;
}
public abstract class Processor : IProcessor
{
public async Task<TProcessor> Run<TProcessor>() where TProcessor : IProcessor
{
await DoWork();
return (TProcessor)(object)this; // works, but yuck
//return (TProcessor)this; // error: "Cannot convert type 'Processor' to 'TProcessor'"
}
protected abstract Task DoWork();
}
public sealed class FooProcessor : Processor
{
protected override Task DoWork() => Task.CompletedTask;
}
这个(TProcessor)(object)this
技巧确实有效,但却是一个丑陋的编译器黑客技术。
我可以修改此代码以依赖编译时检查吗?
如果有另一个类继承自
Processor
,则可能会出现运行时错误,例如BarProcessor
。然后BarProcessor p = new FooProcessor().Run<BarProcessor>();
可以编译,但在运行时会失败。C# 不支持这样的“self”类型(例如,请参阅https://github.com/dotnet/csharplang/issues/5413),但可以通过将类型参数从方法移到类型来改进代码。这将错误的可能性限制在类实现中,并减少了在调用站点指定类型参数的需要。
例如:
现在 的调用者
Run
不能指定错误的类型参数(因为没有),但FooProcessor
仍然可以错误地从 继承Processor<BarProcessor>
。此外,接口IProcessor<TProcessor>
现在显然只能用作约束(where T: IProcessor<T>
),而不是类型。请注意
FooProcessor
和BarProcessor
是密封的。允许从Processor<TProcessor>
子类继承将要求该子类是通用的,例如public abstract class SpecialProcessor<TProcessor> : Processor<TProcessor> where TProcessor : SpecialProcessor<TProcessor>
。