如何使用 F# 中的接口更新和 return 记录

How to update and return a record using an interface in F#

在 C# 中,我正在使用以下我想移至 F# 中的函数:

public virtual void BringToTop(LayersEnum _TopMostLayer)
        {
            if (LayerName == _TopMostLayer)
            {
                ZIndex = 10;
                Visibility = Visibility.Visible;
            }
            else
            {
                ZIndex = DefaultZIndex;
                Visibility = Visibility.Hidden;
            }
        }

假设我有以下 F# 定义,这在 F# 中是如何实现的:

type DocumentRadioButtons = ChartLayer | WritingLayer | ImageLayer | TranscriptionLayer | PublisherLayer

module WritingLayer=

type Model = 
    {  Name: string
       Visibility: bool 
       DefaultZIndex: int
       ZIndex: int
    }
    interface ILayer with 
         member this.Name with get() = this.Name
         member this.Visibility with get() = this.Visibility
         member this.DefaultZIndex with get() = this.DefaultZIndex
         member this.ZIndex with get() = this.ZIndex

type ProgressNoteWin = {
       ConfirmState: ConfirmState option
       Encounter: view_doctor_encounter
       LastName: string
       DocumentLayer: DocumentRadioButtons
       WritingLayer: WritingLayer.Model
       ImageLayer: ImageLayer.Model
       ChartLayer: ChartLayer.Model
      } 

我非常失败的尝试是:

let BringToTop rb (layer:ILayer) = 
                   
                     if rb.ToString() = layer.Name then {layer with Visibility =true} else {layer with Visibility = false }
                

                { m with WritingLayer = BringToTop documentRadioButton m.WritingLayer},

错误:未定义记录标签“可见性”

感谢您的帮助。

#附录:

最终,我想在 WritingLayer.Model、ImageLayer.Model 和 ChartLayer.Model 的不同记录类型上实现接口,用作:

{m with WritingLayer = BringToTop rb m.WritingLayer; 
  ImageLayer = BringToTop rb m.ImageLayer; 
  ChartLayer = rb m.ChartLayer}

感谢您的考虑。

此代码无法按原样运行:

let BringToTop rb (layer:ILayer) = 
    if rb.ToString() = layer.Name then
        { layer with Visibility =true }
    else
        { layer with Visibility = false }

这是因为 F# 不会隐式向下转换为实现该接口的类型。

第一个可能的解决方案:

从传递 ILayer 切换到直接传递 Model。然后你的代码就可以工作了。

第二个可能的解决方案:

我假设既然您有一个接口,您希望将其作为数据类型而不是模型类型本身传递。如果是这种情况,那么我建议使用对象表达式来更新数据,如下所示:

let mkILayer visible (layer: ILayer) =
    { new ILayer with 
         member this.Name = layer.Name
         member this.Visibility = visible
         member this.DefaultZIndex = layer.DefaultZIndex
         member this.ZIndex = layer.ZIndex }
        
        
let BringToTop rb (layer:ILayer) =
    if rb.ToString() = layer.Name then
        mkILayer true layer
    else
        mkILayer false layer

第三种可能的解决方案

如果您对 downcasting 没意见,您可以隐藏您的第一个 layer 定义,如下所示:

let BringToTop rb (layer:ILayer) = 
    if rb.ToString() = layer.Name then
        let layer = layer :?> Model
        { layer with Visibility =true }
    else
        let layer = layer :?> Model
        { layer with Visibility = false }

但通常每当涉及到一个向下转型时,这就是代码味道,所以我真的建议改为构造一个新的接口实例。

我永远无法按照我想要的方式执行此操作——通过使用 ILayer 界面更新和 return 它关联的记录类型。我最感兴趣的是更好的方法。

我最终所做的只是简单地定义了三个特定的函数,每个函数对应一种类型:

let BringToTopWritingLayer rb layer:WritingLayer.Model =
                    if rb.ToString() = (layer :>ILayer).Name 
                    then 
                       {layer with Visibility = true}                  
                    else
                       {layer with Visibility = false} 

                let BringToTopImageLayer rb layer:ImageLayer.Model =
                    if rb.ToString() = (layer :>ILayer).Name 
                    then 
                       {layer with Visibility = true}                  
                    else
                       {layer with Visibility = false} 

                let BringToTopChartLayer rb layer:ChartLayer.Model =
                    if rb.ToString() = (layer :>ILayer).Name 
                    then 
                       {layer with Visibility = true}                  
                    else
                       {layer with Visibility = false} 

用法为:

{ m with DocumentLayer = documentRadioButton; 
                      WritingLayer = BringToTopWritingLayer documentRadioButton m.WritingLayer;
                      ImageLayer = BringToTopImageLayer documentRadioButton m.ImageLayer;
                      ChartLayer = BringToTopChartLayer documentRadioButton m.ChartLayer}

这似乎非常多余,但似乎编译正确。

如有更好的回答,将不胜感激。