抽象类的参数验证
本文关键字:验证 参数 抽象类 | 更新日期: 2023-09-27 18:04:18
我有以下抽象类:
public abstract class FileClient {
public abstract File GetFile(string Path);
public abstract Task<File> GetFileAsync(string Path);
public abstract void MoveFile(string Source, string Destination);
}
我还有多个派生类来提供方法的实现。由于这些方法被声明为公共的,因此这些类中的每一个都必须执行参数的实参验证。例如,两个GetFile方法的方法验证代码如下所示:
if (!IsConnected) {
throw new NotConnectedException();
}
if (Path == null) {
throw new ArgumentNullException(nameof(Path));
}
对所有派生类执行上述参数验证的最佳方法是什么?我个人能想到以下方法,请随意提出其他方法。
1。在每个派生类
中执行验证我个人不喜欢这个解决方案,因为代码重复。
2。实现验证方法
在实用程序类或抽象类本身中实现验证方法,并在每个派生类中调用这些验证方法。我不喜欢这样,因为我需要为每个想要验证的现有方法创建一个新方法。此外,这并不能解决代码重复的问题,因为我仍然需要多次调用这些方法。
3。模板方法设计模式
通过在抽象类的方法中添加验证来实现模板方法设计模式,并定义派生方法将覆盖的新抽象方法。
public File GetFile(string Path) {
...Validation
return DoGetFile(Path);
}
protected abstract File DoGetFile(string Path);
这是我发现的最好的方法,但是我不喜欢我有重复的方法名(除了Do后缀)和描述。
4。使抽象方法成为虚方法
使方法虚化,并在抽象类中实现验证逻辑。派生类需要先调用基方法,然后再执行自己的逻辑。
public abstract class FileClient {
public virtual File GetFile(string Path) {
...Validation
return null;
}
}
public class FtpClient : FileClient {
public override File GetFile(string Path) {
File file = base.GetFile(Path);
if (file != null) {
return file;
}
...Logic
}
}
这种方法非常简洁,但我不喜欢它,原因如下:
- 方法仅在验证时被声明为虚方法。没有逻辑,因为它们总是返回null。
- 派生类不知道如何处理基类返回的结果
- 基类需要为异步方法返回一个非空的Task对象,这将需要被派生类"等待"。从性能的角度来看,这可能重要,也可能无关紧要,这取决于需求。
还有第五种方法使用代码契约
使用代码契约,你可以使用接口契约实现契约设计,在编译时,整个契约将自动注入到整个接口的所有实现中(实际上使用后编译过程)。
你只需要定义一个IFileClient
接口和一个契约类。这可能是最优雅、最强大的解决方案。
参见下面的代码示例:
[ContractClass(typeof(IFileClientContract))]
public interface IFileClient
{
File GetFile(string path);
Task<File> GetFileAsync(string path);
void MoveFile(string source, string destination);
}
[ContractClassFor(typeof(IFileClient))]
public abstract class IFileClientContract : IFileClient
{
public File GetFile(string Path)
{
Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(path));
throw new NotImplementedException();
}
public Task<File> GetFileAsync(string path)
{
Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(path));
throw new NotImplementedException();
}
public void MoveFile(string source, string destination)
{
Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(source));
Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(destination));
throw new NotImplementedException();
}
}
// Any implementation of IFileClient will need to fulfill
// its contracts, including derived classes of FileClient!
public abstract class FileClient : IFileClient
{
public abstract File GetFile(string Path);
public abstract Task<File> GetFileAsync(string Path);
public abstract void MoveFile(string Source, string Destination);
}