我不是C#的新手,但没有Java中那么多的经验。 如您所知,在Java中,我们可以从外部类访问所有私有成员。 所以我在C#中尝试了同样的事情,因为我只需要从我的插件库中访问一些字段和方法,并且不希望它显示给用户。一个简单的例子可以是这样的。
public static class StaticClass {
public class InstanceClass {
private int oldValue;
public int Value;
}
public static void Backup(InstanceClass ic) {
ic.oldValue = ic.Value;
}
public static void Restore(InstanceClass ic) {
ic.Value = ic.oldValue;
}
}
如果我将字段oldValue公开,那么当最终用户使用插件时,它会变得混乱并且看起来很脏。它不必是内部类或某种特定形式。我只想知道是否有任何方法可以控制或访问同一程序集中其他静态类的实例的私有成员。
答案 0 :(得分:3)
仅允许在程序集中使用internal
修饰符。
public class InstanceClass {
internal int oldValue;
public int Value;
}
答案 1 :(得分:3)
这在C#中是不可能的。容器类对嵌套类没有特殊访问权限。
您可以从嵌套类访问容器的私有成员,但反之亦然。您尝试使用的模式并不是在C#中使用 - 它违反了成员的可访问性。有一些黑客可以在C#上强制使用Java模式(使用反射或滥用接口),但它们只是 - 黑客攻击。
"最干净"方法可能看起来像这样:
public static class StaticClass
{
private interface IInstanceClassInternal
{
int OldValue { get; set; }
}
public sealed class InstanceClass : IInstanceClassInternal
{
int IInstanceClassInternal.OldValue { get; set; }
public int Value;
}
public static void Backup(InstanceClass ic)
{
((IInstanceClassInternal)ic).OldValue = ic.Value;
}
public static void Restore(InstanceClass ic)
{
ic.Value = ((IInstanceClassInternal)ic).OldValue;
}
}
很明显,您正在尝试用C#编写Java - 模式,编码风格......这可能是一个坏主意。那些静态方法应该是扩展方法。 "对象中隐藏的功能"并不完全符合C#的OOP概念 - 你的父母不应该自由访问你的内容,它应该只有其他人拥有的相同的公共界面。毕竟,这就是LSP的全部要点 - 这种紧密耦合对于任何可扩展性都非常棘手。如果您希望StaticClass
混淆InstanceClass
es privates,为什么首先将StaticClass
与InstanceClass
分开?只需要Backup
的{{1}}和Restore
个公共成员 - 甚至是界面的一部分(如果你想要"隐藏"它可能来自显式实现) InstanceClass
)的用户。
答案 2 :(得分:1)
您可以使用internal
访问权限修改器,请参阅https://msdn.microsoft.com/en-us/library/ms173121.aspx
内部只能从程序集内部看到
答案 3 :(得分:1)
您是否尝试过“内部”?它将在相同的dll中可用,但不能在外部dll中使用。
public class InstanceClass {
internal int oldValue;
public int Value;
}
答案 4 :(得分:1)
从技术上讲,您可以使用 Reflection (如果您坚持使用private
字段和静态类方法):
using System.Reflection;
...
public static void Backup(InstanceClass ic) {
if (null == ic)
throw new ArgumentNullException("ic");
ic.GetType()
.GetField("oldValue", BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(ic, ic.Value);
}
public static void Restore(InstanceClass ic) {
if (null == ic)
throw new ArgumentNullException("ic");
ic.Value = (int) (ic.GetType()
.GetField("oldValue", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(ic));
}
然而,更好的方法是将访问修饰符从private
更改为internal
:
public class InstanceClass {
internal int oldValue;
public int Value;
}
即使是更好的解决方案,也是将Backup
和Restore
方法移入InstanceClass
:
public class InstanceClass {
private int oldValue;
public int Value;
public void Backup() {
oldValue = Value;
}
public void Restore() {
Value = oldValue;
}
}
答案 5 :(得分:0)
此字段oldValue
是StaticClass
和InstanceClass
的实现细节。让InstanceClass
成为StaticClass
的实现细节,并将接口StaticClass.IInstance
导出到外部客户端:
public static class StaticClass {
public interface IInstance {
int Value { get; set; }
}
private class InstanceClass: IInstance {
public int oldValue;
public Value { get; set; }
}
// Static class becomes responsible for handing out `IInstance` objects
public static IInstance GetInstance() {
return new InstanceClass();
}
public static void Backup(IInstance i) {
if (i is InstanceClass ic) {
ic.oldValue = ic.Value;
}
else {
throw new InvallidOperationException("Can only Backup IInstance objects that were created by GetInstance");
}
}
public static void Restore(IInstance i) {
if (I is InstanceClass ic)
{
ic.Value = ic.oldValue;
}
else {
throw new InvallidOperationException("Can only Restore IInstance objects that were created by GetInstance");
}
}
此解决方案类似于Luaan proposes。但是,它没有使用接口来导出私有数据,而是使用接口来限制公共可用数据。我认为这是一种更干净的设计,没有太多惊喜。
确实会将Value
从字段更改为属性;因此,当您确实需要一个字段时,此模式将不起作用。
OP实例中的静态类使它有些笨拙,并且具有更好的解决方案,但可以在常规类(也许是存储库)中想象一下。在存储库上工作时,在设置存储库中项目的属性且不希望项目包含对存储库或存储库观察者的引用时应通知观察者,这导致我搜索"method only accessible to container class?"到this question。
我打算解决以下问题:
public class Repo
{
public interface IItem
{
int Id { get; }
string MyProperty { get; }
}
private class Item
{
public int Id { get; }
public string MyProperty { get; private set; }
public bool TrySetMyProperty(string newValue)
{
if (!Equals(MyProperty, newValue) &&
IsPreconditionValid())
{
MyProperty = newValue;
return true;
}
else
{
return false;
}
IsPreconditionValid() => true;
}
}
public event EventHandler<EventArgs> OnChanged;
private readonly ConcurrentDictionary<int, Item> items = new ConcurrentDictionary<int, Item>();
public IItem GetOrCreateItemById(int id)
{
bool changed = false;
IItem result = items.GetOrAdd(int, CreateItem);
if (changed)
{
OnChanged?.Invoke(this, EventArgs.Empty);
}
return result;
IItem CreateItem(int key)
{
changed = true;
return new Item() { Id = key };
}
}
public bool TrySetItemMyProperty(int id, string newValue)
{
if (items.TryGet(id, out Item i))
{
if (i.TrySetMyProperty(newValue))
{
OnChanged?.Invoke(this, EventArgs.Empty);
return true;
}
}
return false;
}
}