什么时候以及为什么要使用嵌套类?


30

使用面向对象编程,我们可以在一个类(嵌套类)中创建一个类,但是在我4年的编码经验中,我从未创建过嵌套类。
嵌套类有什么用处?

我知道如果嵌套一个类,则可以将其标记为私有,并且我们可以从包含的类中访问该类的所有私有成员。我们可以将变量作为私有变量包含在包含类本身中。
那么为什么要创建一个嵌套类呢?

在哪些情况下应使用嵌套类,或者在使用方面比其他技术更强大?


1
您有一些很好的答案,有时我会只需要一些在班上只需要的工人阶级或支柱。
狗仔队

Answers:


19

嵌套类的主要特征是它们可以访问外部类的私有成员,同时具有类本身的全部功能。它们也可以是私有的,在某些情况下允许进行一些强大的封装:

在这里,由于该类是私有的,因此我们将设置器完全锁定在工厂内,没有用户可以向下转换它并访问该设置器,并且我们可以完全控制允许的设置。

public interface IFoo 
{
    int Foo{get;}      
}
public class Factory
{
    private class MyFoo : IFoo
    {
        public int Foo{get;set;}
    }
    public IFoo CreateFoo(int value) => new MyFoo{Foo = value};
}

除此之外,它对于在仍可以访问私有成员的受控环境中实现第三方接口很有用。

例如,如果我们要提供到其他对象的某个接口的实例,但又不希望我们的主类实现它,则可以让内部类实现它。

public class Outer
{
    private int _example;
    private class Inner : ISomeInterface
    {
        Outer _outer;
        public Inner(Outer outer){_outer = outer;}
        public int DoStuff() => _outer._example;
    }
    public void DoStuff(){_someDependency.DoBar(new Inner(this)); }
}

在大多数情况下,我希望代表们可以更干净地完成您在第二个示例中所显示的内容
Ben Aaronson

@BenAaronson您将如何使用委托实现随机接口?
Esben Skov Pedersen

@EsbenSkovPedersen好了,您的示例没有传递的实例,而是传递了Outera Func<int>,它只是() => _example
Ben Aaronson

@BenAaronson在这种极其简单的情况下是正确的,但对于更复杂的示例,太多的代表变得笨拙。
Esben Skov Pedersen

@EsbenSkovPedersen:您的示例有一些优点,但是IMO仅应在制作Inner非嵌套且internal不起作用的情况下使用(即,当您不处理其他程序集时)。嵌套类的易读性使其不如使用internal(如果可能)有利。
平坦的

23

通常,每当C需要在内部使用某个永远不应该(直接)在C之外使用的东西时,以及出于某种原因,某个东西需要是一种新型的对象而不是某个现有的对象时,都会在C类的内部创建嵌套的N类。类型。

我相信这种情况最常发生,是实现了一个返回实现某个接口的对象的方法,并且我们希望将该对象的具体类型隐藏起来,因为它在其他任何地方都没有用。

实现IEnumerable是一个很好的例子:

class BlobOfBusinessData: IEnumerable<BusinessDatum>
{
    public IEnumerator<BusinessDatum> GetEnumerator()
    {
         return new BusinessDatumEnumerator(...);
    }

    class BusinessDatumEnumerator: IEnumerator<BusinessDatum>
    {
        ...
    }
}

外界根本没有理由BlobOfBusinessData知道或关心具体BusinessDatumEnumerator类型,因此我们最好将其保留在内部BlobOfBusinessData

那并不意味着它是如何IEnumerable正确实施的“最佳实践”示例,而仅仅是将其传播出去的最低限度的内容,因此我省略了诸如显式IEnumerable.GetEnumerator()方法之类的事情。


6
我与较新的程序员一起使用的另一个示例是.NET中的NodeLinkedList。只要可以访问内容,使用的任何人LinkedList都不会在乎如何Node实现。唯一关心的实体是LinkedList类本身。
法师Xy 2016年

3

那么为什么要创建一个嵌套类呢?

我可以想到几个重要的原因:

1.启用封装

嵌套类通常是该类的实现细节。主要类别的用户不必关心他们的存在。您应该能够随意更改它们,而无需主类的用户更改其代码。

2.避免名字污染

除非在该范围内适当,否则不应在该范围内添加类型,变量,函数等。这与封装完全不同。公开嵌套类型的接口可能很有用,但是嵌套类型的正确位置仍然是主类。在C ++领域中,迭代器类型就是这样的一个例子。我对C#的经验不足,无法为您提供具体的示例。

让我们做一个简单的例子来说明为什么将嵌套类移到与主类相同的作用域是名称污染。假设您正在实现一个链接列表类。通常,您将使用

publid class LinkedList
{
   class Node { ... }
   // Use Node to implement the LinkedList class.
}

如果您决定Node向上移动到与相同的范围LinkedList,则

public class LinkedListNode
{
}

public class LinkedList
{
  // Use LinkedListNode to implement the class
}

LinkedListNode没有LinkedList上课本身就不太可能有用。即使LinkedList提供了一些返回LinkedListNode用户LinkedList可以使用的对象的功能,它也LinkedListNode仅在LinkedList使用时才有用。因此,使“节点”类成为其对等类将LinkedList污染包含范围。


0
  1. 我将公共嵌套类用于相关的帮助程序类。

    public class MyRecord {
        // stuff
        public class Comparer : IComparer<MyRecord> {
        }
        public class EqualsComparer : IEqualsComparer<MyRecord> {
        }
    }
    MyRecord[] array;
    Arrays.sort(array, new MyRecord.Comparer());
    
  2. 将它们用于相关变体。

    // Class that may or may not be mutable.
    public class MyRecord {
        protected string name;
        public virtual String Name { get => name; set => throw new InvalidOperation(); }
    
        public Mutable {
            public override String { get => name; set => name = value; }
        }
    }
    
    MyRecord mutableRecord = new MyRecord.Mutable();
    

呼叫者可以选择哪个版本适合哪个问题。有时,一个类不能一次完成完全构造,并且需要可变版本。处理循环数据时始终如此。稍后可以将变量转换为只读。

  1. 我用它们作内部记录

    public class MyClass {
        List<Line> lines = new List<Line>();
    
        public void AddUser( string name, string address ) => lines.Add(new Line { Name = name, Address = address });
    
        class Line { string Name; string Address; }
    }
    

0

当您要创建一个以上的类实例或要使该类型更可用时,可以使用嵌套类

嵌套类增加了封装,并且将导致更易读和可维护的代码。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.