在F#中命名元组/匿名类型?

问题描述 投票:33回答:6

在C#中,您可以执行以下操作:

var a = new {name = "cow", sound = "moooo", omg = "wtfbbq"};

在Python中你可以做类似的事情

a = t(name = "cow", sound = "moooo", omg = "wtfbbq")

当然不是默认情况下,但实现一个允许你这样做的类t是微不足道的。事实上,当我使用Python时,我确实做到了这一点,并且发现它非常方便小型一次性容器,您希望能够通过名称而不是索引(这很容易混淆)来访问组件。

除了这些细节之外,它们与它们所服务的利基中的元组基本相同。

特别是,我现在正在看这个C#代码:

routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}", // URL with parameters
            new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
        );

它是F#的等价物

type Route = { 
    controller : string
    action : string
    id : UrlParameter }

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    { controller = "Home"; action = "Index"; id = UrlParameter.Optional } // Parameter defaults
  )

这既冗长又重复,更不用说相当烦人了。你有多接近F#中的这种语法?我现在不介意跳过一些箍(甚至燃烧的箍!),如果这意味着它会给我一些有用的东西来干掉像这样的代码。

c# f#
6个回答
24
投票

我发现它更容易

let route = routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}" // URL with parameters
    )
route.Defaults.Add("controller", "Home")
route.Defaults.Add("action", "Index")

要么

[ "controller", "Home"
  "action", "Index" ]
|> List.iter route.Defaults.Add

在F#中,我会避​​免调用接受匿名类型的重载,因为我会避免调用F#方法接受来自C#的FSharpList。这些是特定于语言的功能。通常可以使用与语言无关的重载/解决方法。

EDIT

只看文档 - 这是另一种方法

let inline (=>) a b = a, box b

let defaults = dict [
  "controller" => "Home"
  "action"     => "Index" 
]
route.Defaults <- RouteValueDictionary(defaults)

18
投票

你不能在F#中创建“匿名记录” - 使用类型时,你可以使用匿名的元组,但不带标签,或者你可以使用必须事先声明并有标签的记录:

// Creating an anonymous tuple
let route = ("Home", "Index", UrlParameter.Optional)

// Declaration and creating of a record with named fields
type Route = { controller : string; action : string; id : UrlParameter } 
let route = { controller = "Home"; action = "Index"; id = UrlParameter.Optional } 

从技术上讲,匿名记录的问题是它们必须在某处定义为实际类(.NET运行时需要一个类型),但如果编译器将它们放在每个程序集中,那么具有相同成员的两个匿名记录可能是不同的类型如果它们是在不同的程序集中定义的

老实说,我认为您发布的示例只是ASP.NET中一个糟糕的设计决策 - 它滥用特定的C#功能来做一些它没有设计的东西。它可能没有this那么糟糕,但它仍然很奇怪。该库采用C#匿名类型,但它将其用作字典(即它使用它作为创建键值对的好方法,因为您需要指定的属性是动态的)。

因此,如果你正在使用F#中的ASP.NET,那么在不必创建记录的情况下使用替代方法可能更容易 - 如果ASP.NET API提供了一些替代方法(如Daniel所示,那就更好了)写的方式)。


11
投票

OP没有描述匿名类型的最佳使用。当使用LINQ映射到任意类时,最好使用它们。例如:

var results = context.Students
              .Where(x => x.CourseID = 12)
              .Select(x => new { 
                 StudentID = x.ID, 
                 Name = x.Forename + " " + x.Surname
              });

我知道这可以通过定义一个新的记录类型来完成,但是你有两个地方来维护代码,(1)你已经使用它的记录类型定义(2)。

它可以用元组来完成,但要访问各个字段,你必须始终使用解构语法(studentId, name)。如果元组中有5个项目,这将变得难以处理。我宁愿输入x并点击dot并让intellisense告诉我哪些字段可用。


3
投票

现在在F#4.6(预览版)中我们有Anonymous Records

所以我们可以使用以下代码语法:

let AwesomeAnonymous = {|  ID = Guid.NewGuid()
                           Name = "F#"
                        |}

AwesomeAnonymous.Name |> Debug.WriteLine

它还支持Visual Studio Intellisense:Screenshot

所以代码可能是这样的:

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    {| controller = "Home"; action = "Index"; id = UrlParameter.Optional |} // Parameter defaults
  )

另见:Announcing F# 4.6 Preview


2
投票

这是我对默认Web项目路由配置的看法:

module RouteConfig =

    open System.Web.Mvc
    open System.Web.Routing

    let registerRoutes (routes: RouteCollection) =

        routes.IgnoreRoute("{resource}.axd/{*pathInfo}")

        /// create a pair, boxing the second item
        let inline (=>) a b = a, box b

        /// set the Defaults property from a given dictionary
        let setDefaults defaultDict (route : Route) =  
            route.Defaults <- RouteValueDictionary(defaultDict)

        routes.MapRoute(name="Default", url="{controller}/{action}/{id}")
        |> setDefaults (dict ["controller" => "Home" 
                              "action" => "Index" 
                              "id" => UrlParameter.Optional])

1
投票

正如托尼在答案中指出的那样,F#4.6并没有那么好。使用.NET Core SDK 3.0.100-preview4-011158测试类似示例我能够演示使用新的Anonymous Record功能。至于RouteMap方法,我不熟悉这个API接受的值的类型,但我怀疑下面的例子是可行的。

防爆。

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    {| controller = "Home"; action = "Index"; id = UrlParameter.Optional |} // Parameter defaults
  )

注意在花括号的内侧使用|字符。这就是现在将常规记录与F#中的匿名记录区分开来的原因。

至于你的另一个例子,也许F#示例现在看起来如下所示。

let a = {| name = "cow"; sound = "moooo"; omg = "wtfbbq" |}
© www.soinside.com 2019 - 2024. All rights reserved.