C#:如何在没有 "copy/paste" 代码的情况下重写它
C#: How to rewrite it without "copy/paste" code
我有两个类似的方法:
/// <summary>
///
/// </summary>
/// <param name="domain"></param>
/// <exception cref="DomainRecordNotFoundException">Throw when the dns record is not found in Office365</exception>
/// <exception cref="DomainNotFoundException">Throw when domain is not added to Office365</exception>
/// <exception cref="UnknownException">Unknown exception from Microsoft Graph</exception>
/// <returns></returns>
public async Task<string> GetMxRecordForDomainAsync(string domain)
{
try
{
var records = await _graphClient.Domains[domain].ServiceConfigurationRecords.Request().GetAsync();
string mxRecord = String.Empty;
foreach (var record in records)
{
if (record.RecordType == "Mx")
{
mxRecord = ((Microsoft.Graph.DomainDnsMxRecord)record).MailExchange;
break;
}
}
if (String.IsNullOrWhiteSpace(mxRecord))
throw new DomainRecordNotFoundException(DomainRegistrationCore.Models.DomainRecordType.MX);
return mxRecord;
}
catch (ServiceException graphEx)
{
if (graphEx.StatusCode == System.Net.HttpStatusCode.NotFound)
{
throw new DomainNotFoundException();
}
throw new UnknownException(graphEx.StatusCode, graphEx.Error.Message);
}
}
/// <summary>
///
/// </summary>
/// <param name="domain"></param>
/// <exception cref="DomainRecordNotFoundException">Throw when the dns record is not found in Office365</exception>
/// <exception cref="DomainNotFoundException">Throw when domain is not added to Office365</exception>
/// <exception cref="UnknownException">Unknown exception from Microsoft Graph</exception>
/// <returns></returns>
public async Task<string> GetVerificationRecordForDomainAsync(string domain)
{
try
{
var records = (await _graphClient.Domains[domain].VerificationDnsRecords.Request().GetAsync());
string verificationText = String.Empty;
foreach (var record in records)
{
if (record.RecordType == "Txt")
{
verificationText = ((Microsoft.Graph.DomainDnsTxtRecord)record).Text;
break;
}
}
if (String.IsNullOrWhiteSpace(verificationText))
throw new DomainRecordNotFoundException(DomainRegistrationCore.Models.DomainRecordType.TXT);
return verificationText;
}
catch (ServiceException graphEx)
{
if (graphEx.StatusCode == System.Net.HttpStatusCode.NotFound)
{
throw new DomainNotFoundException();
}
throw new UnknownException(graphEx.StatusCode, graphEx.Error.Message);
}
}
正如我们所见,这两种方法只有这一部分不同:
foreach (var record in records)
{
if (record.RecordType == **RECORD**)
{
mxRecord = ((**TYPE_OF_RECORD**)record).MailExchange;
break;
}
}
if (String.IsNullOrWhiteSpace(mxRecord))
throw new DomainRecordNotFoundException(**RECORD**);
其他部分相同。我想为一种常用方法重写它,但不明白如何。我假设,我可以用 Func<>
或 Action<>
嗯,你可以先写下方法的简化模式并分析它们的结构:
Method1(domain)
some stuff...
foreach if(c1) res = (Type1)smth
if (res == null) throw ex(errMsg1)
Method2(domain)
some stuff...
foreach if(c2) res = (Type2)smth
if (res == null) throw ex(errMsg2)
那么每个功能都有什么?
- 常用代码(包括foreach循环)
- 'if' 语句中的某些特定条件
- 我们将结果转换为的特定类型
- 当结果为 null 或空时抛出的异常我们使用的特定错误消息
我们如何才能满足这些条件并创建一个功能?
- 对于我们列表中的第一名,我们不需要任何东西
- 对于第二个条件,我们可以传递参数,然后在
'if'条件。
- 对于第三种,我们可以使用泛型并将类型作为类型传递
参数或转换结果为
dynamic
- 我们可以再次接受错误信息作为参数
因此,新方法的简化结构可能如下所示:
Method3<TDomainType>(domain, recordTypeToCheck, errorMsg)
some stuff...
foreach if(record.RecordType == recordTypeToCheck) res = (TDomainType)smth
if (res == null) throw ex with errorMsg
首先定义公共接口
public interface IExtractor
{
string RecordType { get; }
string ErrorMessage { get; }
string GetValue(object record);
}
接下来创建实现
class MxRecordExtractor : IExtractor
{
public string RecordType => "Mx";
public string ErrorMessage => DomainRegistrationCore.Models.DomainRecordType.MX;
public string GetValue(object record)
{
return ((Microsoft.Graph.DomainDnsMxRecord)record).MailExchange;
}
}
class VerificationRecordExtractor : IExtractor
{
public string RecordType => "Txt";
public string ErrorMessage => DomainRegistrationCore.Models.DomainRecordType.TXT;
public string GetValue(object record)
{
return ((Microsoft.Graph.DomainDnsTxtRecord)record).Text;
}
}
稍后创建方法的私有抽象版本:
private async Task<string> ExtractForDomainAsync(string domain, IExtractor extractor)
{
try
{
var records = (await _graphClient.Domains[domain].VerificationDnsRecords.Request().GetAsync());
string extractedValue = String.Empty;
foreach (var record in records)
{
if (record.RecordType == extractor.RecordType)
{
extractedValue = extractor.GetValue(record);
break;
}
}
if (String.IsNullOrWhiteSpace(extractedValue))
throw new DomainRecordNotFoundException(extractor.ErrorMessage);
return extractedValue;
}
catch (ServiceException graphEx)
{
if (graphEx.StatusCode == System.Net.HttpStatusCode.NotFound)
{
throw new DomainNotFoundException();
}
throw new UnknownException(graphEx.StatusCode, graphEx.Error.Message);
}
}
最后修改现有的方法来使用我们常用的方法:
public Task<string> GetMxRecordForDomainAsync(string domain)
{
return ExtractForDomainAsync(domain, new MxRecordExtractor());
}
public Task<string> GetVerificationRecordForDomainAsync(string domain)
{
return ExtractForDomainAsync(domain, new VerificationRecordExtractor());
}
我有两个类似的方法:
/// <summary>
///
/// </summary>
/// <param name="domain"></param>
/// <exception cref="DomainRecordNotFoundException">Throw when the dns record is not found in Office365</exception>
/// <exception cref="DomainNotFoundException">Throw when domain is not added to Office365</exception>
/// <exception cref="UnknownException">Unknown exception from Microsoft Graph</exception>
/// <returns></returns>
public async Task<string> GetMxRecordForDomainAsync(string domain)
{
try
{
var records = await _graphClient.Domains[domain].ServiceConfigurationRecords.Request().GetAsync();
string mxRecord = String.Empty;
foreach (var record in records)
{
if (record.RecordType == "Mx")
{
mxRecord = ((Microsoft.Graph.DomainDnsMxRecord)record).MailExchange;
break;
}
}
if (String.IsNullOrWhiteSpace(mxRecord))
throw new DomainRecordNotFoundException(DomainRegistrationCore.Models.DomainRecordType.MX);
return mxRecord;
}
catch (ServiceException graphEx)
{
if (graphEx.StatusCode == System.Net.HttpStatusCode.NotFound)
{
throw new DomainNotFoundException();
}
throw new UnknownException(graphEx.StatusCode, graphEx.Error.Message);
}
}
/// <summary>
///
/// </summary>
/// <param name="domain"></param>
/// <exception cref="DomainRecordNotFoundException">Throw when the dns record is not found in Office365</exception>
/// <exception cref="DomainNotFoundException">Throw when domain is not added to Office365</exception>
/// <exception cref="UnknownException">Unknown exception from Microsoft Graph</exception>
/// <returns></returns>
public async Task<string> GetVerificationRecordForDomainAsync(string domain)
{
try
{
var records = (await _graphClient.Domains[domain].VerificationDnsRecords.Request().GetAsync());
string verificationText = String.Empty;
foreach (var record in records)
{
if (record.RecordType == "Txt")
{
verificationText = ((Microsoft.Graph.DomainDnsTxtRecord)record).Text;
break;
}
}
if (String.IsNullOrWhiteSpace(verificationText))
throw new DomainRecordNotFoundException(DomainRegistrationCore.Models.DomainRecordType.TXT);
return verificationText;
}
catch (ServiceException graphEx)
{
if (graphEx.StatusCode == System.Net.HttpStatusCode.NotFound)
{
throw new DomainNotFoundException();
}
throw new UnknownException(graphEx.StatusCode, graphEx.Error.Message);
}
}
正如我们所见,这两种方法只有这一部分不同:
foreach (var record in records)
{
if (record.RecordType == **RECORD**)
{
mxRecord = ((**TYPE_OF_RECORD**)record).MailExchange;
break;
}
}
if (String.IsNullOrWhiteSpace(mxRecord))
throw new DomainRecordNotFoundException(**RECORD**);
其他部分相同。我想为一种常用方法重写它,但不明白如何。我假设,我可以用 Func<>
或 Action<>
嗯,你可以先写下方法的简化模式并分析它们的结构:
Method1(domain)
some stuff...
foreach if(c1) res = (Type1)smth
if (res == null) throw ex(errMsg1)
Method2(domain)
some stuff...
foreach if(c2) res = (Type2)smth
if (res == null) throw ex(errMsg2)
那么每个功能都有什么?
- 常用代码(包括foreach循环)
- 'if' 语句中的某些特定条件
- 我们将结果转换为的特定类型
- 当结果为 null 或空时抛出的异常我们使用的特定错误消息
我们如何才能满足这些条件并创建一个功能?
- 对于我们列表中的第一名,我们不需要任何东西
- 对于第二个条件,我们可以传递参数,然后在 'if'条件。
- 对于第三种,我们可以使用泛型并将类型作为类型传递
参数或转换结果为
dynamic
- 我们可以再次接受错误信息作为参数
因此,新方法的简化结构可能如下所示:
Method3<TDomainType>(domain, recordTypeToCheck, errorMsg)
some stuff...
foreach if(record.RecordType == recordTypeToCheck) res = (TDomainType)smth
if (res == null) throw ex with errorMsg
首先定义公共接口
public interface IExtractor
{
string RecordType { get; }
string ErrorMessage { get; }
string GetValue(object record);
}
接下来创建实现
class MxRecordExtractor : IExtractor
{
public string RecordType => "Mx";
public string ErrorMessage => DomainRegistrationCore.Models.DomainRecordType.MX;
public string GetValue(object record)
{
return ((Microsoft.Graph.DomainDnsMxRecord)record).MailExchange;
}
}
class VerificationRecordExtractor : IExtractor
{
public string RecordType => "Txt";
public string ErrorMessage => DomainRegistrationCore.Models.DomainRecordType.TXT;
public string GetValue(object record)
{
return ((Microsoft.Graph.DomainDnsTxtRecord)record).Text;
}
}
稍后创建方法的私有抽象版本:
private async Task<string> ExtractForDomainAsync(string domain, IExtractor extractor)
{
try
{
var records = (await _graphClient.Domains[domain].VerificationDnsRecords.Request().GetAsync());
string extractedValue = String.Empty;
foreach (var record in records)
{
if (record.RecordType == extractor.RecordType)
{
extractedValue = extractor.GetValue(record);
break;
}
}
if (String.IsNullOrWhiteSpace(extractedValue))
throw new DomainRecordNotFoundException(extractor.ErrorMessage);
return extractedValue;
}
catch (ServiceException graphEx)
{
if (graphEx.StatusCode == System.Net.HttpStatusCode.NotFound)
{
throw new DomainNotFoundException();
}
throw new UnknownException(graphEx.StatusCode, graphEx.Error.Message);
}
}
最后修改现有的方法来使用我们常用的方法:
public Task<string> GetMxRecordForDomainAsync(string domain)
{
return ExtractForDomainAsync(domain, new MxRecordExtractor());
}
public Task<string> GetVerificationRecordForDomainAsync(string domain)
{
return ExtractForDomainAsync(domain, new VerificationRecordExtractor());
}