C# - 如何避免多次调用

时间:2017-12-01 17:12:44

标签: c# if-statement

我有以下代码

var response = emailValidation();
if(response.Successful) response = emailContentLoad();
if(response.Successful) response = emailPlaceholdersLoad();
// Possibly more calls
if(response.Successful) response = sendEmail();

return response

这四个方法返回一个Response对象,该对象具有Successful属性,指示执行期间是否抛出任何异常。

虽然我确实达到了目的,但我想知道是否有更好的方法可以做到这一点?可能是一种模式,但不会损害可读性。

3 个答案:

答案 0 :(得分:0)

我现在可以想到两种解决方案。

第一个解决方案 - 更接近您显示的代码示例。

在代码示例中,您将在几个步骤中改变相同的变量,具体取决于之前的结果。这可以很容易地表示为操作序列(假设所有方法都具有相同的签名)。这样的事情。

// sequence of the methods that will be chained WITHOUT the first method
var operations = new List<Fuc<...>>()
{
   method2,
   method3,
   method4,
};
// returns the first failed operation or the the last operation from the
// operations list
var result = 
   operations
      .Aggregate(
         seed: method1(), 
         func: (seed, op) => seed.Successful ? op() : seed
      );

通过这种方式,您可以轻松地在操作列表中添加新操作。

如果你不是LINQ的粉丝那么:

var operations = new List<Func<...>>()
{
   method2,
   method3,
   method4,
};

var response = method1();
foreach(var op in operations)
{
  if(response.Successful)
  {
    response = op();
  }
  else 
  {
    break;
  }
}

第二种解决方案 - 可能失败的链接方法的一般解决方案。

您可以查看 Option type (功能语言中流行的概念)

检查此github project以获取c#实施和解释。

This可以给你另一个(lang:F#)

答案 1 :(得分:0)

您也可以写extension methods将这些链接在一起:

Response response = EmailContentLoad().EmailPlaceholdersLoad().SendEmail();

方法看起来像这样:

public static Response EmailContentLoad(this Response response)
{
    if(!response.Successful)
    {
        return response;
    }
    //Do whatever the method normally does
}

这更具可读性和可扩展性,现在您可以按任何顺序调用方法(如果需要),并且您不必维护链式if语句的单个函数。

我做了一个小提琴here来说明。

答案 2 :(得分:-1)

这不是一种模式,但你可以这样写:

Private Sub CreateWordMergeDoc_Click()

On Error GoTo Err_CreateWordMergeDoc_Click

Dim strSQL, strChurch, strDistLang, strFind, strReplace As String
Dim wrdApp As Word.Application
Dim wrdDoc As Word.Document
Dim wrdMergeDoc As Word.Document
Dim strFilepath As String

strFilepath = "O:\Church Phone List"

'Require choice for church and district
If IsNull(Me![ChurchCombo]) = True Then
    MsgBox "Select church", , "Church Phone List"
    Me.ChurchCombo.SetFocus
    GoTo CloseSub
End If

strChurch = Me![ChurchCombo]
strDistLang = Me![DistrictChoiceCombo]

If strDistLang = "" Then
    MsgBox "Select District", , "Church Phone List"
    Me.DistrictChoiceCombo.SetFocus
    GoTo CloseSub
Else
    strDistLang = IIf(Me![DistrictChoiceCombo] = "", "Church", Me![DistrictChoiceCombo])
End If

'Create SQL string from present church/district information
strSQL = "SELECT Churches.* " & vbCrLf & _
"FROM Churches " & vbCrLf & _
"WHERE (((Churches.Church)='" & strChurch & "') AND ((Churches.[District/Language])Like'" & strDistLang & "')) " & vbCrLf & _
"ORDER BY Churches.NAME;"

Set wrdApp = CreateObject("Word.Application")
wrdApp.Visible = True

Set wrdDoc = wrdApp.Documents.Open(strFilepath & "\Phone Merge Document.docx")

With wrdDoc

    With .ActiveWindow
        'Open the header/footer and add the church (and district if appropriate)
        .ActivePane.View.SeekView = WdSeekView.wdSeekCurrentPageHeader
        .Selection.EndKey Unit:=wdLine
        .Selection.TypeText Text:=strChurch & IIf(strDistLang <> "Church", " (" & strDistLang & ")", "")
        'Close header/footer
        .ActivePane.View.SeekView = WdSeekView.wdSeekMainDocument
    End With

    With .MailMerge
        .MainDocumentType = wdCatalog
        .OpenDataSource NAME:= _
            GetNamePath _
            , ConfirmConversions:=False, ReadOnly:=False, LinkToSource:=True, _
            AddToRecentFiles:=False, PasswordDocument:="", PasswordTemplate:="", _
            WritePasswordDocument:="", WritePasswordTemplate:="", Revert:=False, _
            Format:=wdOpenFormatAuto, Connection:= _
            "DSN=MS Access Database;DBQ=" & strFilepath & "2017\Phone List 2017.mdb;DriverId=25;FIL=MS Access;MaxBufferSize=2048;PageTimeout=5;" _
            , SQLStatement:=strSQL, SubType:= _
           wdMergeSubTypeOther

        .Destination = wdSendToNewDocument
        .SuppressBlankLines = True
        With .DataSource
            .FirstRecord = wdDefaultFirstRecord
            .LastRecord = wdDefaultLastRecord
        End With
        .Execute Pause:=False
    End With

    .Close SaveChanges:=wdDoNotSaveChanges

End With

    With wrdApp
        .Selection.WholeStory
        With .Selection.ParagraphFormat
            .SpaceBeforeAuto = False
            .SpaceAfterAuto = False
        End With
        .Selection.ParagraphFormat.TabStops.ClearAll
        .ActiveDocument.DefaultTabStop = InchesToPoints(0.5)
        'Add a tab stop
        .Selection.ParagraphFormat.TabStops.Add Position:=InchesToPoints(0.1), _
            Alignment:=wdAlignTabLeft, Leader:=wdTabLeaderSpaces

        'Replace (C) and (¢) with [C] and [c] since auto replace for (c) may be enabled
        .Selection.Find.Replacement.ClearFormatting
        With .Selection.Find
            .Text = "(C)"
            .Replacement.Text = "[C]"
            .Forward = True
            .Wrap = wdFindContinue
            .Format = False
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        .Selection.Find.Execute Replace:=wdReplaceAll
        With .Selection.Find
            .Text = "(¢)"
            .Replacement.Text = "[c]"
            .Forward = True
            .Wrap = wdFindContinue
            .Format = False
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        .Selection.Find.Execute Replace:=wdReplaceAll

        'Lock document so track changes stays on
        .ActiveDocument.Protect Password:="onebody1", NoReset:=False, Type:= _
             wdAllowOnlyRevisions, UseIRM:=False, EnforceStyleLock:=False
        .ChangeFileOpenDirectory _
         strFilepath & "\Track-Change Documents\"
    End With

    strFind = "/"
    strReplace = " "

    strDistLang = Replace(strDistLang, strFind, strReplace)

     wrdApp.ActiveDocument.SaveAs2 FileName:=strChurch & IIf(strDistLang <> "Church", " - " & strDistLang, ""), FileFormat _
        :=wdFormatXMLDocument, LockComments:=False, Password:="", _
        AddToRecentFiles:=True, WritePassword:="", ReadOnlyRecommended:=False, _
        EmbedTrueTypeFonts:=False, SaveNativePictureFormat:=False, SaveFormsData _
        :=False, SaveAsAOCELetter:=False, CompatibilityMode:=14

    wrdApp.ActiveDocument.Activate

'quit the word application:

wrdApp.Quit

MsgBox "Completed", , "Church Phone List"

CloseSub:

'Clear the object variables:

Set wrdDoc = Nothing
Set wrdApp = Nothing

Exit_CreateWordMergeDoc_Click:
Set wrdDoc = Nothing
Set wrdApp = Nothing
    Exit Sub

Err_CreateWordMergeDoc_Click:
MsgBox Err.Description
    Resume Exit_CreateWordMergeDoc_Click

End Sub

你仍然拥有相同数量的if语句,但是一旦发生错误,函数就会终止,并且将避免不必要的检查。

作为建议,我不会捕获这些方法中的所有异常。相反,我会抓住他们想要处理它或你想向用户显示错误消息的位置。因此,您可以将代码简化为:

var response = emailValidation();
if(!response.Successful) return response;

response = emailContentLoad();
if(!response.Successful) return response;

response = emailPlaceholdersLoad();
if(!response.Successful) return response;

response = sendEmail();
return response