如何访问嵌套类的私有成员?

Posted

技术标签:

【中文标题】如何访问嵌套类的私有成员?【英文标题】:how to get access to private members of nested class? 【发布时间】:2010-05-12 08:07:32 【问题描述】:

背景:我已将(父)类 E 与嵌套类 N 与 E 中的 N 的几个实例封闭在一起。在封闭的(父)类中,我正在做一些计算,并为嵌套类的每个实例设置值。像这样的:

n1.field1 = ...;
n1.field2 = ...;
n1.field3 = ...;
n2.field1 = ...;
...

这是一个大的 eval 方法(在父类中)。我的意图是——因为所有计算都在父类中(它们不能在每个嵌套实例中完成,因为它会使代码更复杂)——使 setter 只对父类和 getter 可用。

现在有一个问题:

当我将设置器设为私有时,父类无法访问它们 当我将它们公开时,每个人都可以更改值 而C#没有朋友的概念 我无法在构造函数中传递值,因为使用了惰性求值机制(因此必须在引用它们时创建实例 -- 我创建所有对象并按需触发计算)

我被卡住了——怎么做(限制访问父类,不多也不少)?


我怀疑我会先得到答案——“但为什么你不按每个字段拆分评估”——所以我举例回答这个问题:你如何计算最小值和最大值收藏?以快速的方式?答案是——一口气。这就是为什么我有一个 eval 函数,它可以一次进行计算并设置所有字段。

【问题讨论】:

内部关键字不够吗? “信任”应该在编码范围之外。代码要么坚如磐石,要么不是,你必须对它给予额外的信任。我更喜欢第一个:-) How to restrict access to nested class member to enclosing class? 的可能重复项 Eric Lippert 的评论令人惊讶和震惊,并说明了为什么 C# 的访问规则如此草率。访问与信任/安全无关......这完全是一个封装问题。嵌套类的成员应该总是可以被包含它们的类访问。他们不在 C# 中是糟糕的语言设计。具有讽刺意味的是,该语言强制内部类对外部类缺乏“信任”。 我也对 Eric Lippert 的评论感到震惊。我什至不相信自己,更不用说我的同事了!我使用可访问性级别来保证对象可能处于的状态。如果您依赖信任,为什么还要设置可访问性级别? 【参考方案1】:

您可以在E 内部声明一个私有接口IN,由N 显式实现。这个接口将暴露N 的成员只能由E 访问:

public class E

    public void Foo()
    
      IN n = new N();
      n.Field1 = 42;
    

    public class N : IN
    
        private int _field1;

        int IN.Field1
        
            get  return _field1; 
            set  _field1 = value; 
        
    

    private interface IN
    
        int Field1  get; set; 
    

【讨论】:

谢谢。接口不能设为私有,因为我不会读取父类之外的值(我需要公共 getter),一旦它是公共的,我也可以公开使用 setter。 我认为您误解了我的回答...IN 界面从未打算公开。这只是嵌套类仅向包含类公开属性的一种方式。如果需要,您还可以在 N 中创建公共 getter 如果接口是私有的,它在父类之外是不可用的。 嗯,对,就是这样……这样,只有父类可以设置嵌套类的私有字段 这个答案是天才。巨大的 +1!【参考方案2】:

如果您可以将父类和子类放在另一个程序集中,您可以使用internal 作为设置器。在野外通常是这样处理的。

编辑

Thomas Levesque's answer 给了我一个想法:

class Program

    static void Main(string[] args)
    
        E myE = new E();

        Console.WriteLine("E.N1.Field1 = " + myE.N1.Field1);
        Console.WriteLine("E.N2.Field1 = " + myE.N2.Field1);
    

    public interface IN
    
        int Field1  get; 
    

    public class E
    
        private N _n1 = new N();
        private N _n2 = new N();

        public E()
        
            _n1.Field1 = 42;
            _n2.Field1 = 23;
        

        public IN N1
        
            get  return _n1; 
        

        public IN N2
        
            get  return _n2; 
        

        private class N : IN
        
            private int _field1;

            public int Field1
            
                get  return _field1; 
                set  _field1 = value; 
            
        
    

根据您需要如何公开子类N,这可能会起作用。

【讨论】:

谢谢,但它是一个文件,不是那么大,所以设置这个单独的程序集似乎有点矫枉过正。 根据 Thomas Levesque 的回答更新了我的回答。 美(界面可嵌套)。谢谢! Thomas Levesque 的回答对我来说似乎有点奇怪。您将嵌套类设为私有,将接口设为公有(而 TL 的代码反之亦然)。这有什么意义?我认为重点是让外部类访问嵌套类的私有字段,同时将嵌套类暴露给外部世界。在您的情况下,外部类没有特殊权限,它可以像其他任何人一样通过接口访问 Field1。如果你想让嵌套类保持私有,接口可能根本不需要,你可以使用属性。【参考方案3】:

另一种选择是将您希望成为私有的成员公开如果您的(嵌套)类是私有的。如果私有类的字段是公共的,它只会暴露给封闭类.

public class E

    public void Foo()
    
      IN n = new N();
      n.field1 = 42;
    

    class N : IN
    
        public int _field1;
    

现在N 只对E 可见,所以n._field1 公开只对E 很重要,你很安全..

【讨论】:

【参考方案4】:

这是一个老问题,但这里有一个不使用接口的可能解决方案。

您可以在内部类中有一个静态函数,它在外部类中设置委托,如下所示:

public class Outer 
    private delegate void _operateDlg(Inner inner, bool value);
    private static _operateDlg _validate;

    static Outer() 
        Inner.Init();
    

    public void Set(Inner inner, bool value) 
        _validate(inner, value);
    

    public class Inner 
        public bool IsValid get; private set; 
        public static void Init() 
            Outer._validate += delegate(Inner i, bool value) 
                i.IsValid = value;
            ;
        
    

您可以将各种不同的委托放入使用 Inner.Init() 方法分配的外部类中,例如通过私有构造函数或特定字段的 getter/setter 返回 Inner 类实例的方法.

如果你不介意在你的内部类中有一个额外的 Init() 静态函数,那么这不必改变。但是如果不希望 Init() 方法可见,可以使用反射来调用它:

using System.Reflection;

public class Outer 
    private delegate void _operateDlg(Inner inner, bool value);
    private static _operateDlg _validate;

    static Outer() 
        typeof(Inner).GetMethod("Init",
            BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null);
    

    public void Set(Inner inner, bool value) 
        _validate(inner, value);
    

    public class Inner 
        public bool IsValid get; private set; 
        private static void Init() 
            Outer._validate = delegate(Inner i, bool value) 
                i.IsValid = value;
            ;
        
    

我知道无论如何都可以使用反射来绕过私有访问限制,但在我看来,仅使用它来调用一个 Init() 方法,然后分配适当的委托是一种更清洁、更通用的解决方案。另一种方法是为您可能想要创建的每个委托调用反射,即使这样也可能存在限制(例如无法为构造函数创建委托)。

上述解决方案不仅支持包装构造函数,而且在程序的生命周期中只使用一次反射,因此除了您使用委托来实现应该实现的目标之外,不应该有明显的性能损失首先被允许作为直接访问。我不知道为什么 C# 不支持这一点,我想不出它不支持的充分理由。

【讨论】:

【参考方案5】:

使字段“受保护的内部”

如果嵌套类是私有的,您可以对这些字段使用“内部”。

【讨论】:

字段不会暴露给整个程序集吗? 在“内部字段和私有类”的情况下:否,因为该类是私有的。另一种情况:是的。但是自己的程序集应该在您的控制之下。通常这不是问题。 其实这一直是个问题——一年后,你可以有同样的最佳意图,但你可能会忘记设计。代码应该反映设计,而不是 cmets 或愿望。 protected internal 表示受保护或内部...比内部更广泛的访问。在任何情况下,protected 在这里都无关紧要,因为嵌套类没有子类。

以上是关于如何访问嵌套类的私有成员?的主要内容,如果未能解决你的问题,请参考以下文章

访问超类的私有成员

Java的静态成员类

Java核心基础知识——Java 中的嵌套类

Java核心基础知识——Java 中的嵌套类

C++拾遗(十三)友元和嵌套类

从嵌套类到包含类的 C# 成员访问 [重复]