1
\$\begingroup\$

I'm wondering wether there is a way to walk through every branch of this switch without using goto

    public static List<KpiGroup> GetKpiGroups(int siteId, int topLevelProjectId, SiteDetailsModel.KpiCategory category, bool applyMapFilters)
    {
        var kpiGroups = new List<KpiGroup>();

        switch (category)
        {
            case SiteDetailsModel.KpiCategory.Any:
            case SiteDetailsModel.KpiCategory.Wbs:
                kpiGroups.Add(KPIGetActiveSite(siteId, topLevelProjectId));
                if (category == SiteDetailsModel.KpiCategory.Any)
                    goto task;
                break;

            case SiteDetailsModel.KpiCategory.Task:
        task:
                kpiGroups.Add(KPIGetTicketStatus(siteId, topLevelProjectId));
                kpiGroups.Add(KPIGetTicketSeverityClosed(siteId, topLevelProjectId));
                kpiGroups.Add(KPIGetTicketSeverityOpen(siteId, topLevelProjectId));
                kpiGroups.Add(KPIGetTicketOwnerOpen(siteId, topLevelProjectId));
                kpiGroups.Add(KPIGetTicketOwnerClosed(siteId, topLevelProjectId));
                kpiGroups.Add(KPIGetTicketActivity(siteId, topLevelProjectId));
                if (category == SiteDetailsModel.KpiCategory.Any)
                    goto sysConfig;
                break;

            case SiteDetailsModel.KpiCategory.SysConfig:
        sysConfig:
                kpiGroups.Add(KPIGetSysConfigOverview(siteId, topLevelProjectId));
                kpiGroups.Add(KPIGetSysConfigDeviceTypes(siteId, topLevelProjectId));
                kpiGroups.Add(KPIGetSysConfigLevel(siteId, topLevelProjectId));
                break;

            default:
                throw new ArgumentOutOfRangeException(String.Format("GetKpiGroups {0}", category.ToString()));
        }

        return kpiGroups;
    }
\$\endgroup\$
5
  • 2
    \$\begingroup\$ To make life easier for reviewers, please add sufficient context to your question. The more you tell us about what your code does and what the purpose of doing that is, the easier it will be for reviewers to help you. See also this meta question \$\endgroup\$
    – Vogel612
    Commented Jan 22, 2015 at 18:39
  • 5
    \$\begingroup\$ You do not need special goto labels. You can instead use the case statements as labels: see the switch reference. \$\endgroup\$
    – Dan Lyons
    Commented Jan 22, 2015 at 18:40
  • \$\begingroup\$ @DanLyons well that's nice. i have never stumbled accross this. usually i use switch to avoid a plentitude of else if, but that's really useful. thanks! \$\endgroup\$
    – Dbl
    Commented Jan 22, 2015 at 22:36
  • \$\begingroup\$ @DanLyons If you turn your comment into an answer i'll accept it. \$\endgroup\$
    – Dbl
    Commented Jan 23, 2015 at 9:37
  • \$\begingroup\$ Could you add some context for the motivation of the question? As I see it, the category can be one of three (Wbs, Task, or SysConfig) or all three, but not two out of three? \$\endgroup\$ Commented Feb 27, 2015 at 8:32

2 Answers 2

6
\$\begingroup\$

As Dan Lyons stated in his comment you could use the goto case ... instead of custom goto labels. Check the example on the link he provided.

If you want to avoid using goto completely. I suggest you take the adding of the items out of the switch/case and place that in methods. And then, in every case you call the appropriate method to add the necessary items. Example:

List<KpiGroup> kpiGroups = new List<KpiGroup>();

public static List<KpiGroup> GetKpiGroups(int siteId, int topLevelProjectId, SiteDetailsModel.KpiCategory category, bool applyMapFilters)
{
    switch (category)
    {
        case SiteDetailsModel.KpiCategory.Wbs: AddWbsGroups(); break;
        case SiteDetailsModel.KpiCategory.Task: AddTaskGroups(); break;
        case SiteDetailsModel.KpiCategory.SysConfig: AddSysConfigGroups(); break;
        case SiteDetailsModel.KpiCategory.Any:
            AddWbsGroups();
            AddTaskGroups();
            AddSysConfigGroups();
            break;
        default:
            throw new ArgumentOutOfRangeException(String.Format("GetKpiGroups {0}", category.ToString()));
    }

    return kpiGroups;
}

public void AddWbsGroups(int siteId, int topLevelProjectId)
{
    kpiGroups.Add(KPIGetActiveSite(siteId, topLevelProjectId));
}

public void AddTaskGroups(int siteId, int topLevelProjectId)
{
    kpiGroups.Add(KPIGetTicketStatus(siteId, topLevelProjectId));
    kpiGroups.Add(KPIGetTicketSeverityClosed(siteId, topLevelProjectId));
    kpiGroups.Add(KPIGetTicketSeverityOpen(siteId, topLevelProjectId));
    kpiGroups.Add(KPIGetTicketOwnerOpen(siteId, topLevelProjectId));
    kpiGroups.Add(KPIGetTicketOwnerClosed(siteId, topLevelProjectId));
    kpiGroups.Add(KPIGetTicketActivity(siteId, topLevelProjectId));
    kpiGroups.Add(KPIGetActiveSite(siteId, topLevelProjectId));
}

public void AddSysConfigGroups(int siteId, int topLevelProjectId)
{
    kpiGroups.Add(KPIGetSysConfigOverview(siteId, topLevelProjectId));
    kpiGroups.Add(KPIGetSysConfigDeviceTypes(siteId, topLevelProjectId));
    kpiGroups.Add(KPIGetSysConfigLevel(siteId, topLevelProjectId));
}

It looks like overkill using the methods but your code still looks clean, readable and maintainable. It's up to you, use this method or the suggestion from Dan Lyons.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ yeah. i'll go with dan here. upvoted anyway for the effort since it's correct. i thought of that solution too, but i was specifically interested in one which involves as little overhead as necessary because i considered it to not be that much of an uncommon case. \$\endgroup\$
    – Dbl
    Commented Jan 22, 2015 at 22:39
  • 1
    \$\begingroup\$ @AndreasMüller Overhead? Method calls are very cheap and you shouldn't worry about their cost except in extreme cases. \$\endgroup\$
    – svick
    Commented Jan 23, 2015 at 0:35
  • 1
    \$\begingroup\$ kpiGroups needs to be static if you use it like this, but results in loosening thread safety. Also, in this way you would need to clear the list at the start of the method. \$\endgroup\$
    – Heslacher
    Commented Jan 23, 2015 at 7:37
  • \$\begingroup\$ @svick alright. maybe the wrong wording. i don't want to introduce methods for the pure sake of having easier control over what methods are called within a control structure. \$\endgroup\$
    – Dbl
    Commented Jan 23, 2015 at 9:35
5
\$\begingroup\$

I think if you combine @Abbas suggestion with an extension method and some enumerators you'll get a fairly elegant solution:

public static class KpiCategoryExtensions
{
    public static List<KpiGroup> GetKpiGroups(this SiteDetailsModel.KpiCategory category, int siteId, int topLevelProjectId, bool applyMapFilters)
    {
        switch (category)
        {
            case SiteDetailsModel.KpiCategory.Wbs:       return GetWbsGroups(siteId, topLevelProjectId).ToList();
            case SiteDetailsModel.KpiCategory.Task:      return GetTaskGroups(siteId, topLevelProjectId).ToList();
            case SiteDetailsModel.KpiCategory.SysConfig: return GetSysConfigGroups(siteId, topLevelProjectId).ToList();
            case SiteDetailsModel.KpiCategory.Any:       return GetWbsGroups(siteId, topLevelProjectId).Concat(GetTaskGroups(siteId, topLevelProjectId)).Concat(GetSysConfigGroups(siteId, topLevelProjectId)).ToList();
            default:
                throw new ArgumentOutOfRangeException(String.Format("GetKpiGroups {0}", category.ToString()));
        }
    }

    private static IEnumerable<KpiGroup> GetWbsGroups(int siteId, int topLevelProjectId)
    {
        yield return KPIGetActiveSite(siteId, topLevelProjectId);
    }

    private static IEnumerable<KpiGroup> GetTaskGroups(int siteId, int topLevelProjectId)
    {
        yield return KPIGetTicketStatus(siteId, topLevelProjectId);
        yield return KPIGetTicketSeverityClosed(siteId, topLevelProjectId);
        yield return KPIGetTicketSeverityOpen(siteId, topLevelProjectId);
        yield return KPIGetTicketOwnerOpen(siteId, topLevelProjectId);
        yield return KPIGetTicketOwnerClosed(siteId, topLevelProjectId);
        yield return KPIGetTicketActivity(siteId, topLevelProjectId);
        yield return KPIGetActiveSite(siteId, topLevelProjectId);
    }

    private static IEnumerable<KpiGroup> GetSysConfigGroups(int siteId, int topLevelProjectId)
    {
        yield return KPIGetSysConfigOverview(siteId, topLevelProjectId);
        yield return KPIGetSysConfigDeviceTypes(siteId, topLevelProjectId);
        yield return KPIGetSysConfigLevel(siteId, topLevelProjectId);
    }
}
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.