将 public 接口实现限制为仅供内部使用

Restricting public interface implementations to internal use only

我有一个继承/extends一个public接口的内部接口:

internal interface IFoo: IList<string>
{
    void FooAdd(string item);
}

我只想允许 IFoo 的成员进行内部访问,进而扩展 IList<string>IFoo的直接成员可以用internal关键字实现,但是由于IList<T>是public,所以我必须显式实现它们:

public class MyClass: IFoo, IReadOnlyList<string>
{
    // IFoo implementations:
    internal void FooAdd(string item)
    {
        ...
    }

    // Explicit IList<string> implementations:
    void IList<string>.Add(string item)
    {
        ...
    }

    // Public IReadOnlyList<string> members:
    public int Count { get { return ... } }
}

现在,由于 IFoo 接口是内部的,我的库的用户无法访问在 MyClass 中声明的 internal 实现的方法,这正是我想要的。但是,他们仍然可以通过转换访问 IList<T> 成员,这是我没想到的:

var myClassObj = new MyClass<string>();
(myClassObj as IList<string>).Add("Haha, gotcha!");

在库之外,我希望编译器会抱怨无法将 myClassObj 转换为 IList<string>,因为 MyClass 仅通过一个实现此接口内部接口。

如果我希望我的 class 实现 public 接口功能,我应该使用其他模式吗?

I would have expected that the compiler would complain that there is no way to cast myClassObj to an IList<string>

编译器无法做到这一点。除了在非常有限的情况下,编译器会恭敬地对待显式强制转换和转换,并假设既然你写了它,你就是认真的。编写强制转换总是在 运行 时失败的代码并不难,即使编译器允许它,即使您可以查看代码并发现它总是失败。

要了解原因,请考虑您的对象始终可以被 object 类型的变量引用。发生这种情况时,编译器不知道对象的声明实现。它无法阻止代码中的强制转换,即使已知强制转换是非法的。

当然,在 运行 时间,唯一重要的是对象是否确实 确实 实现了接口。因此,无论如何,演员阵容甚至都不违法。显式接口实现不会隐藏接口。它只是强加了一个要求,即除非通过该接口类型的表达式,否则不能使用该实现。这根本不是一种控制可访问性的方法。

如果您不希望外部代码在外部代码可以访问的对象中看到您的 IList<T> 实现,那么您实际上必须 隐藏 它从那个代码。唯一的方法是改变你的设计,让你的对象 一个 IList<T> 而不是 一个 IList<T>(即组合与继承),并且 IList<T> 只能由您想要的代码访问(例如,由标记为 internal 的 属性 公开)。

请记住,即便如此,可访问性主要是一种编译时安全功能。完全信任环境中的反射(即在绝大多数托管代码执行的上下文中)将始终允许访问首先访问该对象的任何代码,而不管可访问性如何。限制访问是很好的,目的是鼓励正确使用您的类型,但它本身并不是安全功能(尽管它可以与此类功能结合使用)。