C#在枚举时遇到困难,感觉代码味道不好

时间:2017-11-03 03:39:33

标签: c# dictionary generics enums

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

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

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

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

#include <iostream>
#include <any>


void* enabler = nullptr;

template <
    typename R, typename F,
    typename std::enable_if<std::is_same<typename std::remove_cv<R>::type, std::any>::value>::type*& = enabler,
    typename... Args>
std::any call(F fn, Args&&... args) {
    return fn(std::forward<Args>(args)...);
}


std::any func(int i)
{
    std::cout<<i<<"\n";
    return std::any(1);
}

void func2(int i)
{
    std::cout<<i<<"\n";
}

int func3(int i)
{
    std::cout<<i<<"\n";
    return 200;
}

int main() 
{
    //Return type is an std::any..

    auto rn = call<std::any>(func, 10);
    std::cout<<std::any_cast<int>(rn)<<"\n";

    //Return type is void and cannot be converted to std::any..
    //call<void>(func, 10); //Error here because void type is not `std::any` type..

    //Return type of `func3` is an integer.. `call` implicitly constructs an std::any from that..
    auto res = call<std::any>(func3, 20);
    std::cout<<std::any_cast<int>(res)<<"\n";

    return 0;
}

请注意,密钥是他们的UserID,我不希望将用户可能选择错误部门或错误建筑物的情况列入白名单。这就是它必须匹配两个值的原因。

2 个答案:

答案 0 :(得分:3)

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

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

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和 DEPARTMENTSETTINGS)。所以,让我们把它拉到自己的辅助函数中:

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,我们可以做其他重构,但这可能已经足够好了。

答案 1 :(得分:1)

放手一搏:

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();
    }
}

如果它对您有用,请告诉我,我可以解释一下。我现在必须跑。