F# 类将接口实现为私有成员,为啥?

Posted

技术标签:

【中文标题】F# 类将接口实现为私有成员,为啥?【英文标题】:F# class implements interface as private member, why?F# 类将接口实现为私有成员,为什么? 【发布时间】:2017-01-12 06:11:58 【问题描述】:

我今天决定用 F# 练习,发现一件有趣的事情。我将在 C# 和 F# 中提供类似的代码,但使用 DAO(数据访问对象)具有完全不同的行为:

C#版本:

interface IPoint2D

    int X  get; set; 
    int Y  get; set; 


class Point2D : IPoint2D

    private int _x = 0;
    private int _y = 0;

    public int X  get  return _x;  set  _x = value;  
    public int Y  get  return _y;  set  _y = value;  

F# 版本:

type IPoint2D =
    abstract member X: int
    abstract member Y: int

type Point2D(x: int, y: int) = 
    let _x = 0
    let _y = 0

    interface IPoint2D with
        member this.X = x
        member this.Y = y

第一个非常明显的区别是,使用 C#,我必须将成员声明为 public 以实现合同。

但是,为什么 F# 允许将接口实现为私有成员?有什么意义?

【问题讨论】:

"使用 C#,我必须将成员声明为 public" 不是 - Explicit Interface Implementation (C# Programming Guide) @IvanStoev 这只是另一种方式...您可以欺骗显式实现,例如:IPoint2D pointObj = new Point2D(); pointObj.X = 2; 您可以在 VStudio/VSCode 中尝试... F# 不支持隐式接口实现。您将不得不向上转型:let p = firstPoint :> IPoint2D in p.X @krontogiannis 我明白了...let point = new Point2D(2, 3) let x = (point :> IPoint2D).X,为什么 F# 创建者不想包含 implicit 实现方式? 我认为这是一个特性,而不是错误,因为它不鼓励面向对象的设计。 【参考方案1】:

F# 需要向上转换对象才能直接访问它们的接口。 let x = (firstPoint :> IPoint2D).X

F# 确实支持函数参数的隐式接口转换。它曾经需要泛型,但已在新版本的语言中进行了更新。

let Distance (a:IPoint2D) (b:IPoint2D) =
    (a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y)
    |> float |> Math.Sqrt

let firstPoint = new Point2D(2, 3)
let secondPoint = new Point2D(4, 5)

let distance = Distance firstPoint secondPoint
printf "%f" distance

并非所有人都同意,但作为设计规则,不应向上转换对象以直接访问其接口。这样做会增加代码耦合。如果编写的代码调用对象接口上的方法,则在不更新所有调用视图的情况下无法轻松更改该对象类。

如果接口是带有 x、y、z 的 IPoint3D 并且您想将其更改为 IPoint2D,那么所有引用 z 的转换都必须更新,而不仅仅是像 Distance 这样的接口使用者。

我相信这种设计选择是为了与语言的其余部分保持一致,并在使用 OOP 时促进更松散的耦合。有一个 2014 年的 user voice feature request 请求隐式向上转换没有得到答复。

【讨论】:

以上是关于F# 类将接口实现为私有成员,为啥?的主要内容,如果未能解决你的问题,请参考以下文章

为啥不能隐式实现非公共接口成员?

为啥继承具有名称签名的接口成员的 C# 抽象类至少需要实现其中一个?

C++类封装-公用接口与私有实现的分离

F# 和接口实现的成员

设计模式

接口实现和私有继承之间的交互