c# - 如何使功能更强大?

标签 c# f#

我是一名 c# 人员,最近学习了 JavaScript,并考虑走 f# 道路。我喜欢 JavaScript 的函数式特性,但在将它引入我的 C# 代码时遇到了麻烦。我看了下面的代码块,感觉真的很难看。对于那些天生喜欢函数式编程的人来说,在 c#(甚至 f#)中执行此 block 的更好方法是什么。

(或者我是不是找错了树想改进它)

    /// <summary>
    /// Get the current Tenant based on tenantNameOverride if present, 
    ///   otherwise uses urlHost to figure it out
    /// case independent
    /// if urlHost and tenantNameOverride empty then 
    ///   throws exception
    /// if tenantNameOverride specified and not found
    ///   throw exception
    /// if tenantNameOverride not specified and there 
    ///   is no default tenant, then throw exception
    /// </summary>
    /// <param name="tenants"></param>
    /// <param name="urlHost"></param>
    /// <param name="tenantNameOverride"></param>
    /// <returns></returns>
    private static Tenant GetTenantBasedOnUrlHost(
        List<Tenant> tenants, string urlHost=null,
        string tenantNameOverride=null)
    {
        if (String.IsNullOrEmpty(urlHost) && 
            String.IsNullOrEmpty(tenantNameOverride))
        {
            throw new ApplicationException(
                "urlHost or tenantName must be specified");
        }

        Tenant tenant;
        if (String.IsNullOrEmpty(tenantNameOverride))
        {
            tenant = tenants.
                FirstOrDefault(a => a.DomainName.ToLower().Equals(urlHost)) ??
                     tenants.FirstOrDefault(a => a.Default);
            if (tenant == null)
            {
                throw new ApplicationException
                    ("tenantName must be specified, no default tenant found");
            }
        }
        else
        {
            tenant = tenants.FirstOrDefault
                (a => a.Name.ToLower() == tenantNameOverride.ToLower());
            if (tenant == null)
            {
                throw new ApplicationException
                    ("tenantNameOverride specified and not found");
            }
        }
        return tenant;
    }

/********************* 更新如下 *****************/

根据 Jon Skeet 的建议,我制定了两种方法。除了顶部的空字符串违规错误检查外,我不确定我们是否可以在 C# 中轻松避免。在未找到时抛出异常也感觉有点奇怪,但出于我的目的,这些方法应该始终找到租户,如果找不到,那是意外的,异常似乎是合理的。

这看起来确实更简洁,并且更好地遵循单一职责的设计准则。也许这就是我不喜欢我的解决方案的地方,它与功能性或非功能性无关。

     /// <summary>
    /// Return tenant based on URL (or return default tenant if exists)
    /// </summary>
    /// <param name="tenants"></param>
    /// <param name="urlHost"></param>
    /// <returns></returns>
    private static Tenant GetTenantBasedOnUrl(
        List<Tenant> tenants, string urlHost)
    {
        if (String.IsNullOrEmpty(urlHost))
        {
            throw new ApplicationException(
                "urlHost must be specified");
        }

        var tenant = tenants.
            FirstOrDefault(a => a.DomainName.ToLower().Equals(urlHost)) ??
                     tenants.FirstOrDefault(a => a.Default);
        if (tenant == null)
        {
            throw new ApplicationException
                ("tenant not found based on URL, no default found");
        }
        return tenant;
    }

    /// <summary>
    /// Get exact tenant name match and do not return default even 
    /// if exists.
    /// </summary>
    /// <param name="tenants"></param>
    /// <param name="tenantNameOverride"></param>
    /// <returns></returns>
    private static Tenant GetTenantByName(List<Tenant> tenants,
        string tenantNameOverride)
    {
        if (String.IsNullOrEmpty(tenantNameOverride))
        {
            throw new ApplicationException(
                "tenantNameOverride or tenantName must be specified");
        }

        var tenant = tenants.FirstOrDefault
            (a => a.Name.ToLower() == tenantNameOverride.ToLower());
        if (tenant == null)
        {
            throw new ApplicationException
                ("No tenant Found (not checking for default)");
        }
        return tenant;
    }
}

最佳答案

以下是我在 F# 中解决问题的方法。

首先,如果我正确理解了要求,函数的调用者必须提供一个域名,一个要搜索的租户名称。在 C# 中,这种互斥规则很难建模,这导致必须指定至少一个的规则,但如果指定了两个,则其中一个参数优先。

虽然使用 C# 的类型系统很难定义这样的规则,但在 F# 中声明是微不足道的,使用区分联合:

type TenantCriterion =
| DomainName of Uri
| Name of string

这意味着搜索租户的条件可以是或者 DomainName 或者 Name,但是从来没有。

在我定义的 DomainName 中,我将类型更改为 System.Uri。当您处理 URL 时,使用 Uri 值通常比使用 string 值更安全。

与其将 string 值转换为小写,不如使用 StringComparison.OrdinalIgnoreCase 比较它们更安全,如果这是你想要的,因为有各种微妙的本地化如果你转换的​​问题,例如土耳其语字符串到小写(这种转换是有损的)。

最后,我将查询更改为返回 Tenant option 而不是抛出异常。在函数式编程中,我们更喜欢避免异常。如果你想要比 option 更详细的异常处理,你可以 use the Either monad .

综上所述,这是查找租户功能的可能实现:

let findTenant tenants = function
    | DomainName u ->
        let t = tenants |> List.tryFind (fun x -> x.DomainName = u)
        match t with
        | Some t -> Some t
        | None -> tenants |> List.tryFind (fun x -> x.IsDefault)
    | Name n ->
        tenants
        |> List.tryFind
            (fun x -> n.Equals(x.Name, StringComparison.OrdinalIgnoreCase))

此函数的类型为 Tenant list -> TenantCriterion -> Tenant option。如果您想要更惰性的评估,可以将 List.tryFind 替换为 Seq.tryFind

关于c# - 如何使功能更强大?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33699158/

相关文章:

c# - 将参数传递给 EventHandler

c# - DataGridView 单元格颜色更改

c# - Nuget 将文件从 packages 文件夹复制到项目目录

c# - 使用Kinect SDK,如何得到 "record"红外线信号?

嵌套 bool 测试的 F# 计算表达式?

f# - 如何将函数传递给NUnit Throws Constraint?

asp.net-web-api - 在 F# 中实现 IExceptionHandler 或 IExceptionLogger 等消息处理程序时,返回 "empty task"的正确方法是什么?

wpf - 使用 FSharp.ViewModule 启用对话框确定按钮

c# - WPF MVVM 将项目添加到组合框并更改选定项目

f# - 'T and ' U 在 F# 中是什么意思?