为什么/什么时候应该在 .net 中使用嵌套类?或者你不应该?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/48872/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why/when should you use nested classes in .net? Or shouldn't you?
提问by Eric Haskins
In Kathleen Dollard's 2008 blog post, she presents an interesting reason to use nested classes in .net. However, she also mentions that FxCop doesn't like nested classes. I'm assuming that the people writing FxCop rules aren't stupid, so there must be reasoning behind that position, but I haven't been able to find it.
在Kathleen Dollard 2008 年的博客文章中,她提出了在 .net 中使用嵌套类的有趣原因。但是,她还提到 FxCop 不喜欢嵌套类。我假设编写 FxCop 规则的人并不愚蠢,因此该职位背后一定有推理,但我一直无法找到它。
回答by hazzen
Use a nested class when the class you are nesting is only useful to the enclosing class. For instance, nested classes allow you to write something like (simplified):
当您嵌套的类仅对封闭类有用时,请使用嵌套类。例如,嵌套类允许您编写类似(简化)的内容:
public class SortedMap {
private class TreeNode {
TreeNode left;
TreeNode right;
}
}
You can make a complete definition of your class in one place, you don't have to jump through any PIMPL hoops to define how your class works, and the outside world doesn't need to see anything of your implementation.
您可以在一个地方对您的类进行完整的定义,您不必跳过任何 PIMPL 圈来定义您的类的工作方式,并且外部世界不需要看到您的任何实现。
If the TreeNode class was external, you would either have to make all the fields publicor make a bunch of get/setmethods to use it. The outside world would have another class polluting their intellisense.
如果 TreeNode 类是外部的,您要么必须创建所有字段,public要么创建一堆get/set方法来使用它。外部世界将有另一个类污染他们的智能。
回答by Esteban Araya
Why Use Nested Classes? There are several compelling reasons for using nested classes, among them:
为什么使用嵌套类?使用嵌套类有几个令人信服的理由,其中包括:
- It is a way of logically grouping classes that are only used in one place.
- It increases encapsulation.
- Nested classes can lead to more readable and maintainable code.
- 它是一种对仅在一个地方使用的类进行逻辑分组的方法。
- 它增加了封装性。
- 嵌套类可以产生更具可读性和可维护性的代码。
Logical grouping of classes—If a class is useful to only one other class, then it is logical to embed it in that class and keep the two together. Nesting such "helper classes" makes their package more streamlined.
类的逻辑分组——如果一个类只对另一个类有用,那么将它嵌入该类并将两者保持在一起是合乎逻辑的。嵌套这样的“辅助类”使它们的包更加精简。
Increased encapsulation—Consider two top-level classes, A and B, where B needs access to members of A that would otherwise be declared private. By hiding class B within class A, A's members can be declared private and B can access them. In addition, B itself can be hidden from the outside world.<- This doesn't apply to C#'s implementation of nested classes, this only applies to Java.
增强封装——考虑两个顶级类 A 和 B,其中 B 需要访问 A 的成员,否则这些成员将被声明为私有。通过将 B 类隐藏在 A 类中,A 的成员可以被声明为私有,B 可以访问它们。此外,B 本身可以对外界隐藏。<- 这不适用于 C# 的嵌套类实现,这只适用于 Java。
More readable, maintainable code—Nesting small classes within top-level classes places the code closer to where it is used.
更具可读性、可维护性的代码——在顶级类中嵌套小类使代码更接近使用它的地方。
回答by kay.one
Fully Lazy and thread-safe singleton pattern
完全惰性和线程安全的单例模式
public sealed class Singleton
{
Singleton()
{
}
public static Singleton Instance
{
get
{
return Nested.instance;
}
}
class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
回答by Chris Dail
It depends on the usage. I rarely would ever use a Public nested class but use Private nested classes all of the time. A private nested class can be used for a sub-object that is intended to be used only inside the parent. An example of this would be if a HashTable class contains a private Entry object to store data internally only.
这取决于使用情况。我很少使用公共嵌套类,但一直使用私有嵌套类。私有嵌套类可用于仅在父对象内部使用的子对象。这方面的一个例子是,如果 HashTable 类包含一个私有的 Entry 对象来仅在内部存储数据。
If the class is meant to be used by the caller (externally), I generally like making it a separate standalone class.
如果该类旨在由调用者(外部)使用,我通常喜欢将其设为单独的独立类。
回答by Tyree Hymanson
In addition to the other reasons listed above, there is one more reason that I can think of not only to use nested classes, but in fact public nested classes. For those who work with multiple generic classes that share the same generic type parameters, the ability to declare a generic namespace would be extremely useful. Unfortunately, .Net (or at least C#) does not support the idea of generic namespaces. So in order to accomplish the same goal, we can use generic classes to fulfill the same goal. Take the following example classes related to a logical entity:
除了上面列出的其他原因之外,我还能想到的另一个原因不仅是使用嵌套类,而且实际上是公共嵌套类。对于那些使用共享相同泛型类型参数的多个泛型类的人来说,声明泛型命名空间的能力将非常有用。不幸的是,.Net(或至少 C#)不支持通用命名空间的想法。所以为了实现相同的目标,我们可以使用泛型类来实现相同的目标。以以下与逻辑实体相关的示例类为例:
public class BaseDataObject
<
tDataObject,
tDataObjectList,
tBusiness,
tDataAccess
>
where tDataObject : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
where tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
where tBusiness : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
where tDataAccess : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}
public class BaseDataObjectList
<
tDataObject,
tDataObjectList,
tBusiness,
tDataAccess
>
:
CollectionBase<tDataObject>
where tDataObject : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
where tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
where tBusiness : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
where tDataAccess : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}
public interface IBaseBusiness
<
tDataObject,
tDataObjectList,
tBusiness,
tDataAccess
>
where tDataObject : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
where tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
where tBusiness : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
where tDataAccess : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}
public interface IBaseDataAccess
<
tDataObject,
tDataObjectList,
tBusiness,
tDataAccess
>
where tDataObject : BaseDataObject<tDataObject, tDataObjectList, tBusiness, tDataAccess>
where tDataObjectList : BaseDataObjectList<tDataObject, tDataObjectList, tBusiness, tDataAccess>, new()
where tBusiness : IBaseBusiness<tDataObject, tDataObjectList, tBusiness, tDataAccess>
where tDataAccess : IBaseDataAccess<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
}
We can simplify the signatures of these classes by using a generic namespace (implemented via nested classes):
我们可以通过使用通用命名空间(通过嵌套类实现)来简化这些类的签名:
public
partial class Entity
<
tDataObject,
tDataObjectList,
tBusiness,
tDataAccess
>
where tDataObject : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObject
where tDataObjectList : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObjectList, new()
where tBusiness : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseBusiness
where tDataAccess : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseDataAccess
{
public class BaseDataObject {}
public class BaseDataObjectList : CollectionBase<tDataObject> {}
public interface IBaseBusiness {}
public interface IBaseDataAccess {}
}
Then, through the use of partial classes as suggested by Erik van Brakel in an earlier comment, you can separate the classes into separate nested files. I recommend using a Visual Studio extension like NestIn to support nesting the partial class files. This allows the "namespace" class files to also be used to organize the nested class files in a folder like way.
然后,通过使用 Erik van Brakel 在之前评论中建议的部分类,您可以将类分离到单独的嵌套文件中。我建议使用像 NestIn 这样的 Visual Studio 扩展来支持嵌套部分类文件。这允许“命名空间”类文件也可用于以类似文件夹的方式组织嵌套类文件。
For example:
例如:
Entity.cs
实体.cs
public
partial class Entity
<
tDataObject,
tDataObjectList,
tBusiness,
tDataAccess
>
where tDataObject : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObject
where tDataObjectList : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.BaseDataObjectList, new()
where tBusiness : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseBusiness
where tDataAccess : Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>.IBaseDataAccess
{
}
Entity.BaseDataObject.cs
Entity.BaseDataObject.cs
partial class Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
public class BaseDataObject
{
public DataTimeOffset CreatedDateTime { get; set; }
public Guid CreatedById { get; set; }
public Guid Id { get; set; }
public DataTimeOffset LastUpdateDateTime { get; set; }
public Guid LastUpdatedById { get; set; }
public
static
implicit operator tDataObjectList(DataObject dataObject)
{
var returnList = new tDataObjectList();
returnList.Add((tDataObject) this);
return returnList;
}
}
}
Entity.BaseDataObjectList.cs
Entity.BaseDataObjectList.cs
partial class Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
public class BaseDataObjectList : CollectionBase<tDataObject>
{
public tDataObjectList ShallowClone()
{
var returnList = new tDataObjectList();
returnList.AddRange(this);
return returnList;
}
}
}
Entity.IBaseBusiness.cs
实体.IBaseBusiness.cs
partial class Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
public interface IBaseBusiness
{
tDataObjectList Load();
void Delete();
void Save(tDataObjectList data);
}
}
Entity.IBaseDataAccess.cs
实体.IBaseDataAccess.cs
partial class Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
public interface IBaseDataAccess
{
tDataObjectList Load();
void Delete();
void Save(tDataObjectList data);
}
}
The files in the visual studio solution explorer would then be organized as such:
Visual Studio 解决方案资源管理器中的文件将按如下方式组织:
Entity.cs
+ Entity.BaseDataObject.cs
+ Entity.BaseDataObjectList.cs
+ Entity.IBaseBusiness.cs
+ Entity.IBaseDataAccess.cs
And you would implement the generic namespace like the following:
您将实现如下的通用命名空间:
User.cs
用户.cs
public
partial class User
:
Entity
<
User.DataObject,
User.DataObjectList,
User.IBusiness,
User.IDataAccess
>
{
}
User.DataObject.cs
用户数据对象.cs
partial class User
{
public class DataObject : BaseDataObject
{
public string UserName { get; set; }
public byte[] PasswordHash { get; set; }
public bool AccountIsEnabled { get; set; }
}
}
User.DataObjectList.cs
用户.DataObjectList.cs
partial class User
{
public class DataObjectList : BaseDataObjectList {}
}
User.IBusiness.cs
用户.IBusiness.cs
partial class User
{
public interface IBusiness : IBaseBusiness {}
}
User.IDataAccess.cs
用户.IDataAccess.cs
partial class User
{
public interface IDataAccess : IBaseDataAccess {}
}
And the files would be organized in the solution explorer as follows:
这些文件将在解决方案资源管理器中组织如下:
User.cs
+ User.DataObject.cs
+ User.DataObjectList.cs
+ User.IBusiness.cs
+ User.IDataAccess.cs
The above is a simple example of using an outer class as a generic namespace. I've built "generic namespaces" containing 9 or more type parameters in the past. Having to keep those type parameters synchronized across the nine types that all needed to know the type parameters was tedious, especially when adding a new parameter. The use of generic namespaces makes that code far more manageable and readable.
以上是使用外部类作为通用命名空间的简单示例。过去,我构建了包含 9 个或更多类型参数的“通用命名空间”。必须在所有需要知道类型参数的九种类型之间保持这些类型参数同步是乏味的,尤其是在添加新参数时。通用名称空间的使用使代码更易于管理和可读。
回答by aku
If I understand Katheleen's article right, she proposes to use nested class to be able to write SomeEntity.Collection instead of EntityCollection< SomeEntity>. In my opinion it's controversial way to save you some typing. I'm pretty sure that in real world application collections will have some difference in implementations, so you will need to create separate class anyway. I think that using class name to limit other class scope is not a good idea. It pollutes intellisense and strengthen dependencies between classes. Using namespaces is a standard way to control classes scope. However I find that usage of nested classes like in @hazzen comment is acceptable unless you have tons of nested classes which is a sign of bad design.
如果我理解 Katheleen 的文章是正确的,她建议使用嵌套类来编写 SomeEntity.Collection 而不是 EntityCollection<SomeEntity>。在我看来,这是为您节省一些打字时间的有争议的方式。我很确定在现实世界中的应用程序集合在实现上会有一些差异,因此无论如何您都需要创建单独的类。我认为使用类名来限制其他类的范围不是一个好主意。它污染了智能感知并加强了类之间的依赖关系。使用命名空间是控制类范围的标准方法。但是,我发现在@hazzen 注释中使用嵌套类是可以接受的,除非您有大量嵌套类,这表明设计不佳。
回答by nawfal
I often use nested classes to hide implementation detail. An example from Eric Lippert's answer here:
我经常使用嵌套类来隐藏实现细节。埃里克·利珀特在这里的回答的一个例子:
abstract public class BankAccount
{
private BankAccount() { }
// Now no one else can extend BankAccount because a derived class
// must be able to call a constructor, but all the constructors are
// private!
private sealed class ChequingAccount : BankAccount { ... }
public static BankAccount MakeChequingAccount() { return new ChequingAccount(); }
private sealed class SavingsAccount : BankAccount { ... }
}
This pattern becomes even better with use of generics.See this questionfor two cool examples. So I end up writing
通过使用泛型,这种模式变得更好。有关两个很酷的示例,请参阅此问题。所以我最终写了
Equality<Person>.CreateComparer(p => p.Id);
instead of
代替
new EqualityComparer<Person, int>(p => p.Id);
Also I can have a generic list of Equality<Person>but not EqualityComparer<Person, int>
我也可以有一个通用列表Equality<Person>但不是EqualityComparer<Person, int>
var l = new List<Equality<Person>>
{
Equality<Person>.CreateComparer(p => p.Id),
Equality<Person>.CreateComparer(p => p.Name)
}
where as
然而
var l = new List<EqualityComparer<Person, ??>>>
{
new EqualityComparer<Person, int>>(p => p.Id),
new EqualityComparer<Person, string>>(p => p.Name)
}
is not possible. That's the benefit of nested class inheriting from parent class.
不可能。这就是从父类继承的嵌套类的好处。
Another case (of the same nature - hiding implementation) is when you want to make a class's members (fields, properties etc) accessible only for a single class:
另一种情况(具有相同的性质 - 隐藏实现)是当您想让一个类的成员(字段、属性等)只能被单个类访问时:
public class Outer
{
class Inner //private class
{
public int Field; //public field
}
static inner = new Inner { Field = -1 }; // Field is accessible here, but in no other class
}
回答by supercat
Another use not yet mentioned for nested classes is the segregation of generic types. For example, suppose one wants to have some generic families of static classes that can take methods with various numbers of parameters, along with values for some of those parameters, and generate delegates with fewer parameters. For example, one wishes to have a static method which can take an Action<string, int, double>and yield a String<string, int>which will call the supplied action passing 3.5 as the double; one may also wish to have a static method which can take an an Action<string, int, double>and yield an Action<string>, passing 7as the intand 5.3as the double. Using generic nested classes, one can arrange to have the method invocations be something like:
嵌套类还没有提到的另一个用途是泛型类型的隔离。例如,假设想要拥有一些静态类的泛型系列,这些类可以采用具有不同数量参数的方法,以及其中一些参数的值,并生成具有较少参数的委托。例如,一个人希望有一个静态方法,它可以采用 anAction<string, int, double>和 yield aString<string, int>来调用提供的动作,传递 3.5 作为double; 人们可能还希望有一个静态方法,它可以接受 anAction<string, int, double>并产生 an Action<string>,7作为 theint和5.3作为 the传递double。使用通用嵌套类,可以安排方法调用类似于:
MakeDelegate<string,int>.WithParams<double>(theDelegate, 3.5);
MakeDelegate<string>.WithParams<int,double>(theDelegate, 7, 5.3);
or, because the latter types in each expression can be inferred even though the former ones can't:
或者,因为即使前一个类型不能推断出每个表达式中的后一个类型:
MakeDelegate<string,int>.WithParams(theDelegate, 3.5);
MakeDelegate<string>.WithParams(theDelegate, 7, 5.3);
Using the nested generic types makes it possible to tell which delegates are applicable to which parts of the overall type description.
使用嵌套的泛型类型可以判断哪些委托适用于整个类型描述的哪些部分。
回答by Rachin Goyal
The nested classes can be used for following needs:
嵌套类可用于以下需求:
- Classification of the data
- When the logic of the main class is complicated and you feel like you require subordinate objects to manage the class
- When you that the state and existence of the class fully depends on the enclosing class
- 数据分类
- 当主类的逻辑很复杂,你觉得需要从属对象来管理类时
- 当您认为类的状态和存在完全取决于封闭类时
回答by Eric Dand
I like to nest exceptions that are unique to a single class, ie. ones that are never thrown from any other place.
我喜欢嵌套单个类所独有的异常,即。那些从未从任何其他地方抛出的。
For example:
例如:
public class MyClass
{
void DoStuff()
{
if (!someArbitraryCondition)
{
// This is the only class from which OhNoException is thrown
throw new OhNoException(
"Oh no! Some arbitrary condition was not satisfied!");
}
// Do other stuff
}
public class OhNoException : Exception
{
// Constructors calling base()
}
}
This helps keep your project files tidy and not full of a hundred stubby little exception classes.
这有助于保持您的项目文件整洁,而不是充满一百个粗短的小异常类。

