考虑这个简化的类层次结构:
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),但是可以通过将类型参数从方法移至类型。这限制了类实现中出现错误的可能性,并减少了在调用站点指定类型参数的需要。
例如:
public interface IProcessor<TProcessor> where TProcessor : IProcessor<TProcessor>
{
Task<TProcessor> Run();
}
public abstract class Processor<TProcessor> : IProcessor<TProcessor>
where TProcessor : Processor<TProcessor>
{
public async Task<TProcessor> Run()
{
await DoWork();
return (TProcessor)this;
}
protected abstract Task DoWork();
}
public sealed class FooProcessor : Processor<FooProcessor>
{
protected override Task DoWork() => Task.CompletedTask;
}
public sealed class BarProcessor : Processor<BarProcessor>
{
protected override Task DoWork() => Task.CompletedTask;
}
现在
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>
。