我如何访问其他 类 的私有成员?
How can I access private members from other classes?
我不是 C# 的新手,但经验不如 Java。
如您所知,在 Java 中,我们可以从外部 classes 访问所有私有成员。
所以我在 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 public,那么当最终用户使用该插件时,它会变得一团糟而且看起来很脏。它不必是内部 class 或某种特定形式。我只想知道是否有任何方法可以控制或访问来自同一程序集中其他静态 classes 的实例的私有成员。
要仅允许在程序集内访问,请使用 internal
修饰符。
public class InstanceClass {
internal int oldValue;
public int Value;
}
您可以使用 internal
访问修饰符,请参阅 https://msdn.microsoft.com/en-us/library/ms173121.aspx
Internal 仅在程序集内部可见
你试过"internal"吗?它将在同一个 dll 中可用,但在外部 dll 中不可用。
public class InstanceClass {
internal int oldValue;
public int Value;
}
从技术上讲,您可以使用 Reflection(如果您坚持使用 private
字段和静态 class 方法):
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;
}
}
这在 C# 中是不可能的。容器 class 对嵌套的 class.
没有特殊访问权限
您可以从嵌套的 class 访问容器的私有成员,但反之则不行。您尝试使用的模式根本没有在 C# 中使用——这违反了成员可访问性。有一些技巧可以在 C# 上强制使用 Java 模式(使用反射或滥用接口),但它们只是 - 技巧。
"cleanest" 方法可能如下所示:
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 - 模式、编码风格...这可能是个坏主意。那些静态方法可能应该是扩展方法。 "hidden functionality in an object" 并不完全符合 C# 的 OOP 概念 - 您的 parent 不应该自由访问您的内脏,它应该只真正具有与其他人相同的 public 接口。毕竟,这就是 LSP 的全部意义所在——如此紧密的耦合对于任何可扩展性来说都是相当棘手的。为什么首先要将 StaticClass
与 InstanceClass
分开,如果您希望 StaticClass
与 InstanceClass
es privates 混淆?只需让 Backup
和 Restore
public 成为 InstanceClass
的成员 - 或者甚至是接口的一部分(如果你想 "hide" 它甚至可以通过显式实现来自 InstanceClass
).
的用户
此字段 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 类似。但是它不是使用接口来导出私有数据,而是使用接口来限制公开可用的数据;在我看来,这是一个更简洁的设计,惊喜更少。
它确实将 Value
从字段更改为 属性;所以当你真的需要一个字段时,这个模式就不起作用了。
OP 示例中的静态 class 让它有点尴尬并且有更好的解决方案,但想象一下在常规 class 中,也许在存储库中。在存储库上工作,当设置存储库中项目的属性并且不希望项目包含对存储库或存储库观察者的引用时,应该通知观察者,导致我搜索 "method only accessible to container class?" which led me to .
我打算如下解决:
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;
}
}
我不是 C# 的新手,但经验不如 Java。 如您所知,在 Java 中,我们可以从外部 classes 访问所有私有成员。 所以我在 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 public,那么当最终用户使用该插件时,它会变得一团糟而且看起来很脏。它不必是内部 class 或某种特定形式。我只想知道是否有任何方法可以控制或访问来自同一程序集中其他静态 classes 的实例的私有成员。
要仅允许在程序集内访问,请使用 internal
修饰符。
public class InstanceClass {
internal int oldValue;
public int Value;
}
您可以使用 internal
访问修饰符,请参阅 https://msdn.microsoft.com/en-us/library/ms173121.aspx
Internal 仅在程序集内部可见
你试过"internal"吗?它将在同一个 dll 中可用,但在外部 dll 中不可用。
public class InstanceClass {
internal int oldValue;
public int Value;
}
从技术上讲,您可以使用 Reflection(如果您坚持使用 private
字段和静态 class 方法):
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;
}
}
这在 C# 中是不可能的。容器 class 对嵌套的 class.
没有特殊访问权限您可以从嵌套的 class 访问容器的私有成员,但反之则不行。您尝试使用的模式根本没有在 C# 中使用——这违反了成员可访问性。有一些技巧可以在 C# 上强制使用 Java 模式(使用反射或滥用接口),但它们只是 - 技巧。
"cleanest" 方法可能如下所示:
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 - 模式、编码风格...这可能是个坏主意。那些静态方法可能应该是扩展方法。 "hidden functionality in an object" 并不完全符合 C# 的 OOP 概念 - 您的 parent 不应该自由访问您的内脏,它应该只真正具有与其他人相同的 public 接口。毕竟,这就是 LSP 的全部意义所在——如此紧密的耦合对于任何可扩展性来说都是相当棘手的。为什么首先要将 StaticClass
与 InstanceClass
分开,如果您希望 StaticClass
与 InstanceClass
es privates 混淆?只需让 Backup
和 Restore
public 成为 InstanceClass
的成员 - 或者甚至是接口的一部分(如果你想 "hide" 它甚至可以通过显式实现来自 InstanceClass
).
此字段 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
它确实将 Value
从字段更改为 属性;所以当你真的需要一个字段时,这个模式就不起作用了。
OP 示例中的静态 class 让它有点尴尬并且有更好的解决方案,但想象一下在常规 class 中,也许在存储库中。在存储库上工作,当设置存储库中项目的属性并且不希望项目包含对存储库或存储库观察者的引用时,应该通知观察者,导致我搜索 "method only accessible to container class?" which led me to
我打算如下解决:
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;
}
}