6

F# 3.0 增加了对调用baseprotected成员的更严格检查。我在 C# 中有类似以下抽象类的东西,它具有protected static派生类使用的辅助方法。

public abstract class Processor {
    public abstract void Process();
    protected static void Helper(object arg) { }
}

在 F# 中,其中一个辅助方法作为一等函数传递:

type DerivedProcessor() =
  inherit Processor()

  let init f =
    f ()

  override x.Process() =
    init Processor.Helper

它在 2.0 中编译没有任何问题,但在 3.0 中会产生错误:

调用了受保护的成员或正在使用“base”。这仅在成员的直接实现中允许,因为它们可以逃脱其对象范围。

好的,很容易遵守,只需将调用包装在另一个静态成员中

static member private HelperWrapper(arg) = Processor.Helper(arg)

并通过它。但为什么?

C# 对这种相同的模式没有问题。

public class HappyToCompile : Processor {
    private void Init(Action<object> f) {
        f(null);
    }

    public override void Process() {
        Init(Helper);
    }
}

问题:

  1. 为什么要增加更严格的检查?
  2. (和相关的)这样一个微不足道的解决方法解决了什么可怕的问题?
  3. 这应该鼓励更好的设计吗?
4

1 回答 1

5

使用我的通灵语言设计技能,我猜 F# 2.0 正在生成无法验证的代码。请参阅Eric Lippert 博客上的这篇文章,了解 C# 中的相关问题(自 C# 4, IIRC 以来已修复)。

简而言之,当您创建 F# 函数时,您实际上是在创建一个派生自 的新类FSharpFunc<_,_>,并且从该类中调用受保护的方法是无效的,因为它不在继承链中。

当然,与其禁止这些调用,编译器可以只做你正在做的事情(创建一个私有方法并使用它),但也许人们认为好处不会超过实现这种改进的成本。

于 2012-08-24T16:06:02.737 回答