【问题标题】: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 函数,它可以一次进行计算并设置所有字段。

【问题讨论】:

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

标签: c# design-patterns


【解决方案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 在这里都无关紧要,因为嵌套类没有子类。
      猜你喜欢
      • 2015-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多