如何使功能更强大?

时间:2015-11-13 18:06:33

标签: c# f#

我是一名c#家伙,他最近学习过JavaScript,并考虑沿着f#路走下去。我喜欢JavaScript的功能性,但是很难将它带入我的c#代码中。我看下面的代码块,它真的很难看。对于那些自然倾向于函数式编程的人来说,在c#(甚至f#)中执行此块的更好方法是什么。

(或者我正在吠叫错误的树,希望改善它)

    /// <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;
    }

/ *********************更新以下***************** /

Per 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;
    }
}

1 个答案:

答案 0 :(得分:3)

以下是我如何解决F#中的问题。

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

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

type TenantCriterion =
| DomainName of Uri
| Name of string

这意味着搜索租户的标准可以 一个DomainName 一个名称,但不能两者兼而有之。

在我对DomainName的定义中,我将类型更改为System.Uri。当您处理网址时,使用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