如何从嵌套类中访问封闭类中的字段是最佳方法?

74

假设我有一个表单中的下拉菜单,并且该类中还有另一个嵌套类。那么从嵌套类中访问此下拉菜单的最佳方法是什么?


请详细说明。最好附上示例。 - Craig
学习者,我已经将嵌套类标签添加到你的问题中,因为有一个带有该标签的“何时使用嵌套类”的问题,并且将这两个问题分组比仅使用C#标签更有意义! - Ray Hayes
能够使用标准语法做到这一点的前景非常有趣...有点像闭包的行为。毫无疑问,如果存在这样的功能,它将被滥用直到成为反模式。=( - Sprague
9个回答

93

与Java不同,嵌套类并非特殊的“内部类”,因此您需要传递引用。Raymond Chen在这里描述了其中的区别:C#嵌套类类似于C++嵌套类,而不是Java内部类

以下是一个示例,其中嵌套类的构造函数接收外部类的实例以供稍后引用。

// C#
class OuterClass 
{
    string s;
    // ...
    class InnerClass 
    {
       OuterClass o_;
       public InnerClass(OuterClass o) { o_ = o; }
       public string GetOuterString() { return o_.s; }
    }
    void SomeFunction() {
        InnerClass i = new InnerClass(this);
        i.GetOuterString();
    }

}

请注意,InnerClass可以访问OuterClass的"s"。我没有修改Raymond的代码(如上所链接),所以请记住"string s;"是private的,因为未指定其他访问权限。


34

嵌套类型不像Java中的内部类 - 没有包含类型的实例。(它们更像Java中的静态嵌套类。) 它们是有效地分离的类,有两个区别:

  • 如果包含类型是泛型的,则嵌套类型有效地由包含类型参数化,例如Outer<int>.NestedOuter<string>.Nested不同。
  • 嵌套类型可以访问包含类型中的私有成员。

7
(大约七年后...)您知道为什么外部类无法访问嵌套类的私有或受保护成员,而嵌套类可以访问外部类成员吗? 有时这可能会很有用,我想知道我的设计是否存在缺陷。 - GDS
4
@GDS: 我不知道为什么会做出那个设计决策,但通常我认为这是有道理的。如果将嵌套类设为私有,则在此之后将成员设为内部或公共都是可以的。 - Jon Skeet
2
没错,但是难道不可能有这样的情况:你想要一个内部甚至公共嵌套类,但它的私有和受保护成员对外部类可见吗?那里是否存在潜在的缺陷? - GDS
1
@GDS:我只能说我自己没有遇到过那些情况。我无法揣测设计的确切原因。 - Jon Skeet

13

C#中与Java不同的是,没有对封闭类实例的隐式引用。

你需要将这样的引用传递给嵌套类。一种典型的方法是通过嵌套类的构造函数来实现。

public partial class Form1 : Form
{
    private Nested m_Nested;

    public Form1()
    {
        InitializeComponent();

        m_Nested = new Nested(this);
        m_Nested.Test();
    }

    private class Nested
    {
        private Form1 m_Parent;

        protected Form1 Parent
        {
            get
            {
                return m_Parent;
            }
        }

        public Nested(Form1 parent)
        {
            m_Parent = parent;
        }

        public void Test()
        {
            this.Parent.textBox1.Text = "Testing access to parent Form's control";
        }
    }
}

这似乎也有一个优点,与Java不同,内部类的实例可以访问任何外部类实例的私有成员,而不仅仅是创建内部类实例的外部类实例(即Form1 form = new Form1(); new Nested(form);是合法的)。 - Stevens Miller

11

静态成员

因为迄今为止没有人提到过:根据您的情况,如果成员变量也可以是静态的,则可以通过以下方式简单地访问它。

class OuterClass
{
    private static int memberVar;

    class NestedClass 
    {
        void SomeFunction() { OuterClass.memberVar = 42; }
    }
}

附注:我有意(且多余地)将memberVar标记为private,以说明嵌套类可以访问其外部类的私有成员变量。

注意事项 / 请考虑

某些情况下,这可能是获取访问权限最简单的方法/解决方法之一,但是......

  • 静态也意味着该变量将在所有实例对象之间共享,并带有所有相关的缺点/后果(线程安全性等)

  • 静态还意味着如果您有父类的多个实例,并且该变量应为每个实例保持不同的值,则此方法显然行不通

因此,在大多数情况下,您可能希望采用不同的方法......

传递引用

正如大多数人建议的那样(并且因为它也是最正确的答案),这里是传递外部类实例引用的示例。

class OuterClass
{
    private int memberVar;
    private NestedClass n;

    OuterClass()   { n = new NestedClass(this); }


    class NestedClass
    {
        private OuterClass parent;

        NestedClass(OuterClass p) { parent = p; }
        SomeFunction() { parent.memberVar = 42; }
    }
}

4

另一种在特定情况下有用的方法是将嵌套类派生自外部类。像这样:

class Outer()
{
    protected int outerVar;
    class Nested() : Outer
    {
        //can access outerVar here, without the need for a 
        // reference variable (or the associated dot notation).
    }
}

我在特定情境下尤其使用了这种技术,即结构化单元测试。(虽然这可能不适用于提问者的具体问题,但它通常可用于嵌套类的情况,比如这个“重复”的问题:“我可以在内部类中访问外部类对象吗?”)


现在这是一个有趣的想法,与其他答案有些不同。然而,我非常确定你不能实例化一个现有对象实例的子类,因此如果你已经有一个 Outer 实例并且想要一个对应于你现有 Outer 实例的 Nested 实例,我认为这不起作用。 - Sam
这里的问题在于创建一个新的 'Nested' 实例将调用外部类的 'ctor',很可能导致堆栈溢出。 - BillW

1

如果我理解错了,请纠正我,您正在尝试从内部类处理外部控件,因此遇到了这个问题。更好的方法是以事件驱动的方式处理事务。使用观察者模式,在外部控件上注册一个监听器(您的嵌套/内部类将成为监听器)。这样会使生活更简单。恐怕这不是您期望的答案!


1
你可以将包含类作为参数传递给嵌套类构造函数,像这样:
private NestedClass _nestedClass;
public ParentClass() 
{
   _nestedClass = new NestedClass(this);
}

嵌套类通常不建议使用,应该是私有的和/或内部的。但在我看来,它们有时是有用的。


0
将主类作为构造函数参数发送到嵌套(内部)类中。

-1

上面有一个好的回答,但我想写点东西。

c# 嵌套类默认为 private

对于包含类是私有的如果您要使用它必须将其设置为 public


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接