【问题标题】:C# Having a tough time with enumerations, have a feeling of bad code smellC# 在枚举方面遇到了困难,有一种代码味道不好的感觉
【发布时间】:2017-11-03 03:39:33
【问题描述】:

我很难弄清楚如何做到这一点,基本上我有一个应用程序,它使用枚举来确定此人所在的建筑物,然后他们属于哪个部门。

根据人员所在的部门,可以添加不同的组。我想让它只执行一次,直到他们关闭并重新打开应用程序,有点像白名单。

问题在于,当想要使用泛型方法时,枚举似乎是一个巨大的痛苦。

我能够找到一种方法来做到这一点,使用通用静态字典,但它看起来很糟糕,我不知道是否有我忽略的东西或者更简单的方法来做我想要的。

private async Task CheckWhiteList(PowerShell power, KeyValuePair<string, Enum> history)
        {
            switch ((Building)Model.BuildingSelectedIndex)
            {
                case Building.CaneRidge:
                    history = new KeyValuePair<string, Enum>(Model.UserID, (CaneRidgeSettings.Departments)Model.DepartmentSelectedIndex);
                    if (!WhiteList.Contains(history))
                    {
                        WhiteList.Add(history.Key, history.Value);
                        await power.InvokeAsync();
                    }
                    break;
                case Building.Carothers:
                    history = new KeyValuePair<string, Enum>(Model.UserID, (CarothersSettings.Departments)Model.DepartmentSelectedIndex);
                    if (!WhiteList.Contains(history))
                    {
                        WhiteList.Add(history.Key, history.Value);
                        await power.InvokeAsync();
                    }
                    break;
                case Building.CSC:
                    history = new KeyValuePair<string, Enum>(Model.UserID, (CSCSettings.Departments)Model.DepartmentSelectedIndex);
                    if (!WhiteList.Contains(history))
                    {
                        WhiteList.Add(history.Key, history.Value);
                        await power.InvokeAsync();
                    }
                    break;
                case Building.HQ:
                    history = new KeyValuePair<string, Enum>(Model.UserID, (HQSettings.Departments)Model.DepartmentSelectedIndex);
                    if (!WhiteList.Contains(history))
                    {
                        WhiteList.Add(history.Key, history.Value);
                        await power.InvokeAsync();
                    }
                    break;
                default:
                    break;
            }

请注意,关键是他们的用户 ID,我不想将用户可能选择错误部门或错误建筑物的情况列入白名单。这就是为什么它必须匹配两个值。

【问题讨论】:

  • 能否请您出示您的“通用静态字典”代码?我正要建议这种事情作为解决方案,但想先看看你是如何做到的。
  • public static Dictionary WhiteList = new Dictionary();所有的 Dictionary 就是,只使用 TValue 的基本 Enum 类
  • 那为什么看起来很糟糕?
  • 因为完成这项工作的唯一方法是把它放在一个 switch 语句中,如上所示,有很多重复的代码。我不确定是否有更好的方法来处理这种情况。
  • 我已经发布了一个没有开关且没有任何重复代码的答案。

标签: c# dictionary generics enums


【解决方案1】:

当我看到重复时,我通常会尝试将代码拉出到辅助函数中。

我看到这种模式重复了几次:

case BUILDING:
    history = new KeyValuePair<string, Enum>(Model.UserID, (DEPARTMENTSETTINGS.Departments)Model.DepartmentSelectedIndex);
    if (!WhiteList.Contains(history))
    {
        WhiteList.Add(history.Key, history.Value);
        await power.InvokeAsync();
    }
    break;

每次只有两个位发生变化(BUILDING 和 部门设置)。所以,让我们把它拉到它自己的辅助函数中:

private async Task CheckWhiteList(PowerShell power)
{
    switch ((Building)Model.BuildingSelectedIndex)
    {
        case Building.CaneRidge:
            await InvokeScriptIfNotYetWhiteListedAsync(power, Model.UserID, (CaneRidgeSettings.Departments)Model.DepartmentSelectedIndex);
            break;

        case Building.Carothers:
            await InvokeScriptIfNotYetWhiteListedAsync(power, Model.UserID, (CarothersSettings.Departments)Model.DepartmentSelectedIndex);
            break;

        case Building.CSC:
            await InvokeScriptIfNotYetWhiteListedAsync(power, Model.UserID, (CSCSettings.Departments)Model.DepartmentSelectedIndex);
            break;

        case Building.HQ:
            await InvokeScriptIfNotYetWhiteListedAsync(power, Model.UserID, (HQSettings.Departments)Model.DepartmentSelectedIndex);
            break;

        default:
            break;
    }
}

private async Task InvokeScriptIfNotYetWhiteListedAsync(PowerShell power, string userID, Enum department)
{
    var history = new KeyValuePair<string, Enum>(userID, department);
    if (!WhiteList.Contains(userID))
    {
        WhiteList.Add(history.Key, history.Value)
        await power.InvokeAsync()
    }
}

但仍有重复的代码。如果我们取消对InvokeScriptIfNotYetWhiteListedAsync() 的呼叫会怎样?

我还在帮助函数中看到我们正在创建一个KeyValuePair 只是为了调用.Contains()。我们可以改用.ContainsKey() 来完全避免创建KeyValuePair 对象。

private async Task CheckWhiteList(PowerShell power)
{
    Enum department;
    switch ((Building)Model.BuildingSelectedIndex)
    {
        case Building.CaneRidge:
            department = (CaneRidgeSettings.Departments)Model.DepartmentSelectedIndex;
            break;

        case Building.Carothers:
            department = (CarothersSettings.Departments)Model.DepartmentSelectedIndex;
            break;

        case Building.CSC:
            department = (CSCSettings.Departments)Model.DepartmentSelectedIndex;
            break;

        case Building.HQ:
            department = (HQSettings.Departments)Model.DepartmentSelectedIndex;
            break;

        default:
            return;
    }

    await InvokeScriptIfNotYetWhiteListedAsync(power, Model.UserID, department);
}

private async Task InvokeScriptIfNotYetWhiteListedAsync(PowerShell power, string userID, Enum department)
{
    // I find compound conditionals sometimes are easier to read if they're
    // given a name before being used.
    var alreadyWhiteListed = WhiteList.ContainsKey(userID) && WhiteList[userID] == department;
    if (!alreadyWhiteListed)
    {
        WhiteList.Add(userID, department)
        await power.InvokeAsync()
    }
}

现在 switch 语句只是选择了正确的 Enum 值,我们也可以把它拉到一个帮助器中并给它一个好名字:

private async Task CheckWhiteList(PowerShell power)
{
    Enum department = GetDepartmentEnumForBuilding((Building)Model.BuildingSelectedIndex);
    await InvokeScriptIfNotYetWhiteListedAsync(power, Model.UserID, department);
}

private Enum GetDepartmentEnumForBuilding(Building building)
{
    switch (building)
    {
        case Building.CaneRidge:
            return (CaneRidgeSettings.Departments)Model.DepartmentSelectedIndex;

        case Building.Carothers:
            return (CarothersSettings.Departments)Model.DepartmentSelectedIndex;

        case Building.CSC:
            return (CSCSettings.Departments)Model.DepartmentSelectedIndex;

        case Building.HQ:
            return (HQSettings.Departments)Model.DepartmentSelectedIndex;

        default:
            throw new ArgumentOutOfRangeException(nameof(building));
    }
}

private async Task InvokeScriptIfNotYetWhiteListedAsync(PowerShell power, string userID, Enum department)
{
    var alreadyWhiteListed = WhiteList.ContainsKey(userID) && WhiteList[userID] == department;
    if (!alreadyWhiteListed)
    {
        WhiteList.Add(userID, department)
        await power.InvokeAsync()
    }
}

现在似乎没有太多重复的代码,每个函数做的工作也更少,所以可能更容易理解。

如果我们不想使用 Enum,我们还可以进行其他重构,但这可能已经足够了。

【讨论】:

  • 正是这种方法。
  • 你可以随时重构 case 语句中的代码 :) 正是我想要的建议
  • 不知道为什么我没有想到为每个 switch 案例制作另一种方法。我现在感觉很愚蠢....但是关于仅通过键过滤,我实际上需要检查两个值,而不仅仅是白名单中的一个。在这种情况下每次都创建一个对象可以接受吗?
  • 在这种情况下,我可能会使用history.TryGetValue(key, out value),或history.ContainsKey(),然后测试history[key]。我已经更新了示例。
【解决方案2】:

试一试:

public static Dictionary<Building, Func<int, Enum>> mySwitch = new Dictionary<Building, Func<int, Enum>>()
{
    { Building.CaneRidge, n => (CaneRidgeSettings.Departments)n },
    { Building.Carothers, n => (CarothersSettings.Departments)n },
    { Building.CSC, n => (CSCSettings.Departments)n },
    { Building.HQ, n => (HQSettings.Departments)n },
};

private async Task CheckWhiteList(PowerShell power)
{
    var history = new KeyValuePair<string, Enum>(Model.UserID, mySwitch[(Building)Model.BuildingSelectedIndex](Model.DepartmentSelectedIndex));
    if (!WhiteList.Contains(history))
    {
        WhiteList.Add(history.Key, history.Value);
        await power.InvokeAsync();
    }
}

如果它对你有用,请告诉我,我可以再解释一下。不过我现在得跑了。

【讨论】:

  • 好的,我知道大部分情况下它是如何工作的。我想创建两个字典将是处理该代码的最干净的方法。我明天看看能不能把它插上,看看能不能让它工作。从技术上讲,我不是我工作的程序员,只是自学和边走边学。欣赏这个例子。
  • 是的,这确实有效,这可能是最短的方法。我什至没有想过在字典中使用委托,谢谢。这将替代方案缩短了 50 多行代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-12
  • 2016-06-24
  • 1970-01-01
  • 1970-01-01
  • 2019-08-09
  • 2021-11-05
相关资源
最近更新 更多