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

时间:2010-05-12 08:07:32

标签: c# design-patterns

背景:我已经使用嵌套类N封装了(父)E类,在E中有几个N实例。在封闭的(父)类中,我正在进行一些计算,我正在为嵌套类的每个实例设置值。像这样:

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

这是一个大的eval方法(在父类中)。我的意图是 - 因为所有计算都在父类中(它们不能按照嵌套实例进行,因为它会使代码更复杂) - 使setter仅对父类和getter公共可用。

现在有一个问题:

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

我被困 - 怎么做(限制访问父类,不多也不少)?


我怀疑我会先回答问题 - “但是为什么你不按每个字段拆分评估” - 所以我通过例子回答这个问题:你如何计算最小值和最大值一个集合?快点?答案是 - 一次通过。这就是为什么我有一个eval函数可以进行计算并一次设置所有字段。

5 个答案:

答案 0 :(得分:48)

您可以在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; }
    }
}

答案 1 :(得分:7)

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

<强> 修改

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的方式,这可能会有效。

答案 2 :(得分:7)

另一种方法是,如果您的(嵌套)类是私有的,请将您希望成为私有的成员如果私有类的字段是公共的,则它只会暴露给封闭类

public class E
{
    public void Foo()
    {
      IN n = new N();
      n.field1 = 42;
    }

    class N : IN
    {
        public int _field1;
    }
}

现在N仅对E可见,因此n._field1公开只对E有意义,而且您是安全的..

答案 3 :(得分:1)

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

你可以在内部类中使用静态函数来设置外部类中的委托,如下所示:

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()方法分配的外部类中,例如通过私有构造函数返回Inner类实例的方法或特定字段的getter / setter 。

如果你不介意在你的内部类中有一个额外的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;
            };
        }
    }
}

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

上面的解决方案不仅支持包装构造函数,而且它只会在程序的生命周期中使用Reflection一次,因此除了使用委托实现应该使用的事实之外,不应该有明显的性能损失。首先允许直接访问。我不知道为什么C#不支持这个,我想不出有什么理由不这样做。

答案 4 :(得分:-2)

将字段设为“受保护的内部”

如果嵌套类是私有的,则可以对这些字段使用obally“internal”。