摘要的参数验证 class
Argument validation of abstract class
我有以下摘要class:
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);
}
我还有多个派生的 classes 提供方法的实现。由于这些方法被声明为 public,因此这些 class 中的每一个都必须执行参数的参数验证。例如,两个 GetFile 方法的方法验证代码如下所示:
if (!IsConnected) {
throw new NotConnectedException();
}
if (Path == null) {
throw new ArgumentNullException(nameof(Path));
}
对所有派生的 classes 执行上述参数验证的最佳方法是什么?我个人能想到的有以下几种方式,欢迎大家补充。
1。在每个派生的 class
中执行验证
我个人不喜欢这个解决方案,因为代码重复。
2。实施验证方法
在实用程序 class 或抽象 class 本身中实施验证方法,并在每个派生的 class 中调用这些验证方法。我不喜欢这个,因为我需要为每个要验证的现有方法使用一个新方法。此外,这并没有解决代码重复问题,因为我仍然需要多次调用这些方法。
3。模板方法设计模式
通过在抽象方法中添加验证来实现 template method design 模式 class,并定义派生方法将覆盖的新抽象方法。
public File GetFile(string Path) {
...Validation
return DoGetFile(Path);
}
protected abstract File DoGetFile(string Path);
这是我找到的最好的方法,但我不喜欢我有重复的方法名称(除了 Do 后缀)和描述。
4。将抽象方法设为虚拟
使方法虚拟化并在抽象中实现验证逻辑class。派生 classes 将需要先调用基方法,然后再执行自己的逻辑。
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
}
}
这种方法很干净,但我不喜欢它,原因如下:
- 方法被声明为虚拟仅用于验证。没有逻辑,因为它们总是 return null.
- 派生的 classes 将不知道如何处理由基础 class 编辑的结果 return。
- Base class 需要 return 异步方法的非空 Task 对象,它需要 "awaited" 派生 classes。从性能的角度来看,这可能重要也可能无关紧要,具体取决于要求。
还有第五种方法使用 code contracts。
使用代码契约,您可以使用接口契约实现 design by contract,其中整个契约将在编译时自动注入到整个接口的所有实现中(实际上使用 post-编译过程)。
你只需要定义一个IFileClient
接口和一个契约class。这可能是最优雅、最强大的解决方案。
参见以下代码示例:
[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);
}
我有以下摘要class:
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);
}
我还有多个派生的 classes 提供方法的实现。由于这些方法被声明为 public,因此这些 class 中的每一个都必须执行参数的参数验证。例如,两个 GetFile 方法的方法验证代码如下所示:
if (!IsConnected) {
throw new NotConnectedException();
}
if (Path == null) {
throw new ArgumentNullException(nameof(Path));
}
对所有派生的 classes 执行上述参数验证的最佳方法是什么?我个人能想到的有以下几种方式,欢迎大家补充。
1。在每个派生的 class
中执行验证我个人不喜欢这个解决方案,因为代码重复。
2。实施验证方法
在实用程序 class 或抽象 class 本身中实施验证方法,并在每个派生的 class 中调用这些验证方法。我不喜欢这个,因为我需要为每个要验证的现有方法使用一个新方法。此外,这并没有解决代码重复问题,因为我仍然需要多次调用这些方法。
3。模板方法设计模式
通过在抽象方法中添加验证来实现 template method design 模式 class,并定义派生方法将覆盖的新抽象方法。
public File GetFile(string Path) {
...Validation
return DoGetFile(Path);
}
protected abstract File DoGetFile(string Path);
这是我找到的最好的方法,但我不喜欢我有重复的方法名称(除了 Do 后缀)和描述。
4。将抽象方法设为虚拟
使方法虚拟化并在抽象中实现验证逻辑class。派生 classes 将需要先调用基方法,然后再执行自己的逻辑。
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
}
}
这种方法很干净,但我不喜欢它,原因如下:
- 方法被声明为虚拟仅用于验证。没有逻辑,因为它们总是 return null.
- 派生的 classes 将不知道如何处理由基础 class 编辑的结果 return。
- Base class 需要 return 异步方法的非空 Task 对象,它需要 "awaited" 派生 classes。从性能的角度来看,这可能重要也可能无关紧要,具体取决于要求。
还有第五种方法使用 code contracts。
使用代码契约,您可以使用接口契约实现 design by contract,其中整个契约将在编译时自动注入到整个接口的所有实现中(实际上使用 post-编译过程)。
你只需要定义一个IFileClient
接口和一个契约class。这可能是最优雅、最强大的解决方案。
参见以下代码示例:
[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);
}