我是一名 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/