将方法作为参数传递

时间:2018-04-10 09:48:46

标签: c++11 mfc

我已经回顾了这个优秀的answer,但我仍感到困惑。最终我将有10个方法都表现相似。以下是我已经写过的三个例子:

void CChristianLifeMinistryEditorDlg::OnSwapWithChairmanAssignment(UINT nID)
{
    // We need to locate the name for this assignment 
    auto iter = m_mapSwapAssignments.find(nID);

    if (iter != m_mapSwapAssignments.end())
    {
        // We found it
        CChristianLifeMinistryEntry *pReplacement = m_mapSwapAssignments[nID];
        CString strExistingName = _T(""), strReplacementName = _T("");
        GetDlgItemText(m_iSwapAssignmentSourceID, strExistingName);

        CString strPrompt = _T("");
        strPrompt.Format(_T("Swap assignments:\n\n%s - %s\n%s - %s\n\nPlease confirm."),
            m_pEntry->GetMeetingDateAsString(),
            (LPCTSTR)strExistingName,
            pReplacement->GetMeetingDateAsString(),
            (LPCTSTR)pReplacement->GetChairman());
        if (AfxMessageBox(strPrompt, MB_ICONQUESTION | MB_YESNO) == IDYES)
        {
            strReplacementName = pReplacement->GetChairman();
            pReplacement->SetChairman(strExistingName);

            SetDlgItemText(m_iSwapAssignmentSourceID, strReplacementName);
            SetModified(true);
        }
    }
}

void CChristianLifeMinistryEditorDlg::OnSwapWithOpenPrayerAssignment(UINT nID)
{
    // We need to locate the name for this assignment 
    auto iter = m_mapSwapAssignments.find(nID);

    if (iter != m_mapSwapAssignments.end())
    {
        // We found it
        CChristianLifeMinistryEntry *pReplacement = m_mapSwapAssignments[nID];
        CString strExistingName = _T(""), strReplacementName = _T("");
        GetDlgItemText(m_iSwapAssignmentSourceID, strExistingName);

        CString strPrompt = _T("");
        strPrompt.Format(_T("Swap assignments:\n\n%s - %s\n%s - %s\n\nPlease confirm."),
            m_pEntry->GetMeetingDateAsString(),
            (LPCTSTR)strExistingName,
            pReplacement->GetMeetingDateAsString(),
            (LPCTSTR)pReplacement->GetOpenPrayer());
        if (AfxMessageBox(strPrompt, MB_ICONQUESTION | MB_YESNO) == IDYES)
        {
            strReplacementName = pReplacement->GetOpenPrayer();
            pReplacement->SetOpenPrayer(strExistingName);

            SetDlgItemText(m_iSwapAssignmentSourceID, strReplacementName);
            SetModified(true);
        }
    }
}

void CChristianLifeMinistryEditorDlg::OnSwapWithClosePrayerAssignment(UINT nID)
{
    // We need to locate the name for this assignment 
    auto iter = m_mapSwapAssignments.find(nID);

    if (iter != m_mapSwapAssignments.end())
    {
        // We found it
        CChristianLifeMinistryEntry *pReplacement = m_mapSwapAssignments[nID];
        CString strExistingName = _T(""), strReplacementName = _T("");
        GetDlgItemText(m_iSwapAssignmentSourceID, strExistingName);

        CString strPrompt = _T("");
        strPrompt.Format(_T("Swap assignments:\n\n%s - %s\n%s - %s\n\nPlease confirm."),
            m_pEntry->GetMeetingDateAsString(),
            (LPCTSTR)strExistingName,
            pReplacement->GetMeetingDateAsString(),
            (LPCTSTR)pReplacement->GetClosePrayer());
        if (AfxMessageBox(strPrompt, MB_ICONQUESTION | MB_YESNO) == IDYES)
        {
            strReplacementName = pReplacement->GetClosePrayer();
            pReplacement->SetClosePrayer(strExistingName);

            SetDlgItemText(m_iSwapAssignmentSourceID, strReplacementName);
            SetModified(true);
        }
    }
}

我想将每个事件处理程序中的代码编码到方法中。

  • GetChairman
  • SetChairman
  • GetOpenPrayer
  • SetOpenPrayer
  • GetClosePrayer
  • SetClosePrayer

我试过这个:

void CChristianLifeMinistryEditorDlg::SwapAssignments(UINT nID, CString(*GetExistingName), void(*SetReplacementName)(CString))
{
    // We need to locate the name for this assignment 
    auto iter = m_mapSwapAssignments.find(nID);

    if (iter != m_mapSwapAssignments.end())
    {
        // We found it
        CChristianLifeMinistryEntry *pReplacement = m_mapSwapAssignments[nID];
        CString strExistingName = _T(""), strReplacementName = _T("");
        GetDlgItemText(m_iSwapAssignmentSourceID, strExistingName);

        CString strPrompt = _T("");
        strPrompt.Format(_T("Swap assignments:\n\n%s - %s\n%s - %s\n\nPlease confirm."),
            m_pEntry->GetMeetingDateAsString(),
            (LPCTSTR)strExistingName,
            pReplacement->GetMeetingDateAsString(),
            (LPCTSTR)pReplacement->(*GetExistingName));
        if (AfxMessageBox(strPrompt, MB_ICONQUESTION | MB_YESNO) == IDYES)
        {
            strReplacementName = pReplacement->(*GetExistingName);
            pReplacement->(*SetReplacementName)(strExistingName);

            SetDlgItemText(m_iSwapAssignmentSourceID, strReplacementName);
            SetModified(true);
        }
    }
}

我遇到构建错误:

Build Errors

如何将这些方法作为参数传递?

我看到我希望可以使用的另一个answer,但问题是被调用的方法是一个对象的成员。所以它不会允许它。

当前解决方法

目前我已实施了几项功能:

CString CChristianLifeMinistryEntry::GetName(SwapAssignment eAssignment)
{
    switch (eAssignment)
    {
    case SwapAssignment::Chairman:
        return GetChairman();
    case SwapAssignment::Counsellor1:
        return GetAuxiliaryCounsellor1();
    case SwapAssignment::Counsellor2:
        return GetAuxiliaryCounsellor2();
    case SwapAssignment::OpenPrayer:
        return GetOpenPrayer();
    case SwapAssignment::Treasures1:
        return GetTreasures1();
    case SwapAssignment::Treasures2:
        return GetTreasures2();
    case SwapAssignment::Living1:
        return GetLiving1();
    case SwapAssignment::Living2:
        return GetLiving2();
    case SwapAssignment::ConductorCBS:
        return GetCBSConductor();
    case SwapAssignment::ReaderCBS:
        return GetCBSReader();
    case SwapAssignment::ClosePrayer:
        return GetClosePrayer();
    }

    return _T("");
}

// AJT v18.1.6
void CChristianLifeMinistryEntry::SetName(CString strName, SwapAssignment eAssignment)
{
    switch (eAssignment)
    {
    case SwapAssignment::Chairman:
        SetChairman(strName);
        break;
    case SwapAssignment::Counsellor1:
        SetAuxiliaryCounsellor1(strName);
        break;
    case SwapAssignment::Counsellor2:
        SetAuxiliaryCounsellor2(strName);
        break;
    case SwapAssignment::OpenPrayer:
        SetOpenPrayer(strName);
        break;
    case SwapAssignment::Treasures1:
        SetTreasures1(strName);
        break;
    case SwapAssignment::Treasures2:
        SetTreasures2(strName);
        break;
    case SwapAssignment::Living1:
        SetLiving1(strName);
        break;
    case SwapAssignment::Living2:
        SetLiving2(strName);
        break;
    case SwapAssignment::ConductorCBS:
        SetCBSConductor(strName);
        break;
    case SwapAssignment::ReaderCBS:
        SetCBSReader(strName);
        break;
    case SwapAssignment::ClosePrayer:
        SetClosePrayer(strName);
        break;
    }
}

然后我将我提到的代码稍微调整一下,移动到一个新方法中:

// AJT v18.1.6
void CChristianLifeMinistryEditorDlg::SwapAssignments(UINT nID, SwapAssignment eAssignment)
{
    // We need to locate the name for this assignment 
    auto iter = m_mapSwapAssignments.find(nID);

    if (iter != m_mapSwapAssignments.end())
    {
        // We found it
        CChristianLifeMinistryEntry *pReplacement = m_mapSwapAssignments[nID];
        CString strExistingName = _T(""), strReplacementName = _T("");
        GetDlgItemText(m_iSwapAssignmentSourceID, strExistingName);

        CString strPrompt = _T("");
        strPrompt.Format(_T("Swap assignments:\n\n%s - %s\n%s - %s\n\nPlease confirm."),
            m_pEntry->GetMeetingDateAsString(),
            (LPCTSTR)strExistingName,
            pReplacement->GetMeetingDateAsString(),
            (LPCTSTR)pReplacement->GetName(eAssignment));
        if (AfxMessageBox(strPrompt, MB_ICONQUESTION | MB_YESNO) == IDYES)
        {
            strReplacementName = pReplacement->GetName(eAssignment);
            pReplacement->SetName(strExistingName, eAssignment);
            if (pReplacement == m_pEntry) // Swapping assignments on same meeting!
                SetDlgItemText(IDC_COMBO_OCLM_CBS_READER, strExistingName);

            SetDlgItemText(m_iSwapAssignmentSourceID, strReplacementName);
            SetModified(true);
        }
    }
}

所以现在我的11个处理程序看起来与此类似:

// AJT v18.1.6
void CChristianLifeMinistryEditorDlg::OnSwapWithCBSReaderAssignment(UINT nID)
{
    SwapAssignments(nID, SwapAssignment::ReaderCBS);
}

有效。但我看到我的问题中添加了一条新评论,并附有链接,所以我会看看我是否可以到达任何地方。

1 个答案:

答案 0 :(得分:1)

因此,让我们分阶段重构代码。

第1阶段: 首先,在通用函数OnSwapWith

中隔离常用功能
template<typename Function>
void CChristianLifeMinistryEditorDlg::OnSwapWith(Function && function,
                                                 UINT        nID)
{
    // We need to locate the name for this assignment
    auto iter = m_mapSwapAssignments.find(nID);

    if (iter != m_mapSwapAssignments.end())
    {
        // We found it
        CChristianLifeMinistryEntry *pReplacement = m_mapSwapAssignments[nID];
        CString strExistingName = _T(""), strReplacementName = _T("");
        GetDlgItemText(m_iSwapAssignmentSourceID, strExistingName);

        CString strPrompt = _T("");
        strPrompt.Format(_T("Swap assignments:\n\n%s - %s\n%s - %s\n\nPlease confirm."),
            m_pEntry->GetMeetingDateAsString(),
            (LPCTSTR)strExistingName,
            pReplacement->GetMeetingDateAsString(),
            (LPCTSTR)pReplacement->GetChairman());

        if (AfxMessageBox(strPrompt, MB_ICONQUESTION | MB_YESNO) == IDYES)
        {
            function();
            SetDlgItemText(m_iSwapAssignmentSourceID, strReplacementName);
            SetModified(true);
        }
    }
}

void CChristianLifeMinistryEditorDlg::OnSwapWithChairmanAssignment(UINT nID)
{
    auto ChairmanAssignment = [&]() // capture by reference (i.e. &)
    {
        strReplacementName = pReplacement->GetChairman();
        pReplacement->SetChairman(strExistingName);
    };
    CChristianLifeMinistryEditorDlg::OnSwapWith(ChairmanAssignment, nID);
}

void CChristianLifeMinistryEditorDlg::OnSwapWithOpenPrayerAssignment(UINT nID)
{
    auto OpenPrayerAssignment = [&]() // capture by reference (i.e. &)
    {
        strReplacementName = pReplacement->GetOpenPrayer();
        pReplacement->SetOpenPrayer(strExistingName);
    };
    CChristianLifeMinistryEditorDlg::OnSwapWith(OpenPrayerAssignment, nID);
}

void CChristianLifeMinistryEditorDlg::OnSwapWithClosePrayerAssignment(UINT nID)
{
    auto ClosePrayerAssignment = [&]() // capture by reference (i.e. &)
    {
        strReplacementName = pReplacement->GetClosePrayer();
        pReplacement->SetClosePrayer(strExistingName);
    };
    CChristianLifeMinistryEditorDlg::OnSwapWith(ClosePrayerAssignment, nID);
}

// and similarly other functions can be defined

在重构之后,我们至少减少了一级冗余。

在这次重构中,我们通过引用在行中捕获了所有内容:
auto ChairmanAssignment = [&]() {};

通过引用捕获时,所有内容都被捕获,例如this指针 这就是为什么这句话有效:
strReplacementName = pReplacement->GetChairman();

它扩展为:
this->strReplacementName = pReplacement->GetChairman();

第2阶段: 理解你所使用的基本上只是一些通用的 getters setters

所以让我们进一步重构:

// instead of passing one function, we will pass two : getter and setter
template<typename GetterFunction, 
         typename SetterFunction>
void CChristianLifeMinistryEditorDlg::OnSwapWith(GetterFunction && getter,
                                                 SetterFunction && setter,
                                                 UINT              nID)
{
    ...

        if (AfxMessageBox(strPrompt, MB_ICONQUESTION | MB_YESNO) == IDYES)
        {
            strReplacementName = getter(pReplacement);
            setter(pReplacement, strExistingName);
            SetDlgItemText(m_iSwapAssignmentSourceID, strReplacementName);
            SetModified(true);
        }

    ...
}

和来电方

void CChristianLifeMinistryEditorDlg::OnSwapWithChairmanAssignment(UINT nID)
{
    auto getter = [](CChristianLifeMinistryEntry * pReplacement) 
    { // remove the captures now, since we are getting the state with the pointer
        return pReplacement->GetChairman();
    };
    auto setter = [](CChristianLifeMinistryEntry * pReplacement,
                     CString               const & strExistingName) 
    {
        pReplacement->SetChairman(strExistingName);
    };
    CChristianLifeMinistryEditorDlg::OnSwapWith(getter, setter, nID);
}

最终工作代码

11个菜单事件处理程序看起来像:

void CChristianLifeMinistryEditorDlg::OnSwapWithChairmanAssignment(UINT nID)
{
    auto getter = [](CChristianLifeMinistryEntry * pReplacement)
    {
        return pReplacement->GetChairman();
    };
    auto setter = [](CChristianLifeMinistryEntry * pReplacement, 
                     CString                     & strExistingName)
    {
        pReplacement->SetChairman(strExistingName);
    };
    CChristianLifeMinistryEditorDlg::OnSwapWith(getter, setter,
                                                nID, IDC_COMBO_OCLM_CHAIRMAN);
}

模板化功能现在看起来像:

template<typename GetterFunction, typename SetterFunction>
void CChristianLifeMinistryEditorDlg::OnSwapWith(GetterFunction && getter,
                                                 SetterFunction && setter,
                                                 UINT              nID,
                                                 UINT              nReplacementCtrlID)
{
    // We need to locate the name for this assignment
    auto iter = m_mapSwapAssignments.find(nID);

    if (iter != m_mapSwapAssignments.end())
    {
        // We found it
        CChristianLifeMinistryEntry *pReplacement = m_mapSwapAssignments[nID];
        CString strExistingName = _T(""), strReplacementName = _T("");
        GetDlgItemText(m_iSwapAssignmentSourceID, strExistingName);

        CString strPrompt = _T("");
        strPrompt.Format(_T("Swap assignments:\n\n%s - %s\n%s - %s\n\nPlease confirm."),
            m_pEntry->GetMeetingDateAsString(),
            (LPCTSTR)strExistingName,
            pReplacement->GetMeetingDateAsString(),
            (LPCTSTR)getter(pReplacement));

        if (AfxMessageBox(strPrompt, MB_ICONQUESTION | MB_YESNO) == IDYES)
        {
            strReplacementName = getter(pReplacement);
            setter(pReplacement, strExistingName);
            if (pReplacement == m_pEntry) // Swapping assignments on same meeting!
                SetDlgItemText(nReplacementCtrlID, strExistingName);

            SetDlgItemText(m_iSwapAssignmentSourceID, strReplacementName);
            SetModified(true);
        }
    }
}