将 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
的 属性 公开)。
请记住,即便如此,可访问性主要是一种编译时安全功能。完全信任环境中的反射(即在绝大多数托管代码执行的上下文中)将始终允许访问首先访问该对象的任何代码,而不管可访问性如何。限制访问是很好的,目的是鼓励正确使用您的类型,但它本身并不是安全功能(尽管它可以与此类功能结合使用)。
我有一个继承/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 anIList<string>
编译器无法做到这一点。除了在非常有限的情况下,编译器会恭敬地对待显式强制转换和转换,并假设既然你写了它,你就是认真的。编写强制转换总是在 运行 时失败的代码并不难,即使编译器允许它,即使您可以查看代码并发现它总是失败。
要了解原因,请考虑您的对象始终可以被 object
类型的变量引用。发生这种情况时,编译器不知道对象的声明实现。它无法阻止代码中的强制转换,即使已知强制转换是非法的。
当然,在 运行 时间,唯一重要的是对象是否确实 确实 实现了接口。因此,无论如何,演员阵容甚至都不违法。显式接口实现不会隐藏接口。它只是强加了一个要求,即除非通过该接口类型的表达式,否则不能使用该实现。这根本不是一种控制可访问性的方法。
如果您不希望外部代码在外部代码可以访问的对象中看到您的 IList<T>
实现,那么您实际上必须 隐藏 它从那个代码。唯一的方法是改变你的设计,让你的对象 有 一个 IList<T>
而不是 是 一个 IList<T>
(即组合与继承),并且 IList<T>
只能由您想要的代码访问(例如,由标记为 internal
的 属性 公开)。
请记住,即便如此,可访问性主要是一种编译时安全功能。完全信任环境中的反射(即在绝大多数托管代码执行的上下文中)将始终允许访问首先访问该对象的任何代码,而不管可访问性如何。限制访问是很好的,目的是鼓励正确使用您的类型,但它本身并不是安全功能(尽管它可以与此类功能结合使用)。