【问题标题】:F# type constraints on enums枚举上的 F# 类型约束
【发布时间】:2013-02-27 14:50:06
【问题描述】:

我正在尝试定义一个从字符串到枚举的通用转换运算符,我想像这样使用它:

let day = asEnum<DayOfWeek>("Monday")

但是有了这个实现:

let asEnum<'a, 'b when 'a: (new : unit -> 'a) and 'a : struct and 'a :> ValueType and 'a : enum<'b>> text = 
    match Enum.TryParse<'a>(text)  with
    | true, value -> Some value
    | false, _ -> None

我只能这样使用:

    let day = asEnum<DayOfWeek,_>("Monday")

或者这个:

    let day:DayOfWeek option = asEnum("Monday")

如果我从类型约束中完全省略'a : enum&lt;'b&gt;,我可以随意使用它,但是如果有人不指定类型,它将默认为int,我真的不喜欢,我希望它像我指定约束时一样给出编译时错误

也许只指定一个类型参数并推断另一个类型参数有什么技巧?有什么想法吗?

【问题讨论】:

    标签: f# enums


    【解决方案1】:

    不幸的是,为了增加约束,您似乎必须将其全部拼写出来:(正如 kvb 指出的那样,您可以通过添加 'T : enum&lt;int&gt; 来避免在 TryParse 上重复约束尖括号外的约束)

    这也有效:

    let asEnum<'T 
      when 'T : enum<int>
      and 'T : struct
      and 'T :> ValueType
      and 'T : (new : unit -> 'T)> text =
      match Enum.TryParse<'T>(text) with
      | true, value -> Some value
      | _ -> None
    

    如果底层类型不是int,则会出现编译时错误:

    type ByteEnum =
      | None = 0uy
    
    asEnum<ByteEnum> "None" //ERROR: The type 'int' does not match the type 'byte'
    

    【讨论】:

      【解决方案2】:

      这个怎么样?

      let asEnum s :'a option when 'a:enum<'b> =
          match System.Enum.TryParse s with
          | true, v -> Some v
          | _ -> None
      
      // works, but warns that type params shouldn't be given explicitly
      asEnum<System.Reflection.BindingFlags,_> "DeclaredOnly"    
      // also okay
      (asEnum "DeclaredOnly" : System.Reflection.BindingFlags option)
      

      【讨论】:

      • 天哪。我什至不知道这是有效的语法。我想如果你把它改成'a:enum&lt;int&gt; 那会给他他想要的。他也可以通过let e : System.Reflection.BindingFlags option = asEnum "DeclaredOnly" 来避免警告。
      • 为什么这行得通,但在 &lt; &gt; 之间设置相同的约束却不行?
      • @Daniel - 我认为@ovastus 不希望 int 被强制执行,他希望尽可能推断它(确实如此)。
      • 这在实现方面要好得多,但在使用方面,实际上更糟,因为除了仍然不必指定 _ 额外类型参数之外,它总是会给出警告 :( I'我试图优化使用,即使 asEnum 的定义非常笨拙。仍然很酷,你在外面时不必把它全部吐出来
      • @ovatsus:在大多数情况下,放置良好的类型注释不需要显式类型参数,因此这不是问题,例如:match asEnum "Black" with Some e -&gt; e : ConsoleColor | None -&gt; failwith "!"
      【解决方案3】:

      3年后的小更新^_^

      使用此字符串扩展将字符串转换为枚举

      type System.String with
              /// Strongly-typed shortcut for Enum.TryParse(). 
              member this.ToEnum<'a when 'a :> System.Enum and 'a : struct and 'a : (new: unit -> 'a)> () =
                  let ok, v = System.Enum.TryParse<'a>(this, true)
                  if ok then Some v else None    
      

      小心你的枚举声明。

      type failingEnum =
                  | FirstValue
                  | SecondValue
                  | AnotherValue
      
      type compliantEnum =
                  | FirstValue = 0
                  | SecondValue = 1
                  | AnotherValue = 2
      

      然后

      let x = "whatever".ToEnum<failingEnum>(); 
      //- will give error failingEnum is not compatible with the type System.Enum
      
      let x = "whatever".ToEnum<compliantEnum>(); 
      //- will succeed !
      

      【讨论】:

      • 从技术上讲,failingEnum 是联合类型而不是枚举。这就是它失败的原因。
      • 当然...感谢您的评论。这就是我添加评论的原因,因为人们经常将枚举定义为联合。不久前我是其中的一员:)
      【解决方案4】:

      我尝试的另一件事是:

      type AsEnum = 
      
          static member Get<'a, 'b when 'a: (new : unit -> 'a) and 'a : struct and 'a :> ValueType and 'a : enum<int>> (text:string) =
      
              match Enum.TryParse<'a>(text) with
              | true, value -> Some value
              | _ -> None
      
          static member Get<'a, 'b when 'a: (new : unit -> 'a) and 'a : struct and 'a :> ValueType and 'a : enum<int64>> (text:string) =
      
              match Enum.TryParse<'a>(text) with
              | true, value -> Some value
              | _ -> None
      
      let a = AsEnum.Get<BindingFlags>.Get "DeclaredOnly"   
      

      尝试查看是否可以让编译器推断要调用哪个重载,但如果失败并出现歧义错误

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-19
        • 1970-01-01
        • 1970-01-01
        • 2021-09-23
        • 1970-01-01
        相关资源
        最近更新 更多