在 C# 中使用内部类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/804453/
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
Using Inner classes in C#
提问by DevinB
What are the best practices regarding the use and structure of inner classes in C#.
关于 C# 中内部类的使用和结构的最佳实践是什么?
For instance if I have a very large base class and two large inner classes should I split them up into separate (partial class) codefiles or leave them as one very large unwieldy codefile?
例如,如果我有一个非常大的基类和两个大的内部类,我应该将它们拆分为单独的(部分类)代码文件还是将它们保留为一个非常大的笨拙的代码文件?
Also is it bad practice to have an abstract class, with a public inherited inner class?
拥有一个带有公共继承内部类的抽象类也是不好的做法吗?
采纳答案by STW
Typically I reserve inner-classes for one of two purposes:
通常,我出于以下两个目的之一保留内部类:
Public classes which derive from their parent class where the parent class is an abstract base implementation with one or more abstract methods and each subclass is an implementation which serves a specific implementation. after reading Framework Design and Guidelines I see that this is marked as "Avoid", however I use it in scenarios similar to enums--althogh that's probably giving a bad impression as well
The inner classes are private and are units of business logic or otherwise tightly coupled to their parent class in a manner in which they are fundamentally broken when consumed or used by any other class.
从其父类派生的公共类,其中父类是具有一个或多个抽象方法的抽象基本实现,每个子类是一个为特定实现提供服务的实现。阅读框架设计和指南后,我看到这被标记为“避免”,但是我在类似于枚举的场景中使用它-尽管这也可能给人留下不好的印象
内部类是私有的,是业务逻辑的单元,或者以其他方式与它们的父类紧密耦合,当它们被任何其他类使用或使用时,它们从根本上被破坏。
For all other cases I try to keep them in the same namespace and the same accessibility level as their consumer/logical parent--often with names that are a little less friendly than the "main" class.
对于所有其他情况,我尝试将它们保留在与它们的使用者/逻辑父级相同的命名空间和相同的可访问性级别中——通常使用的名称比“主”类更不友好。
On big projects you'd be surprised how often you may find yourself initially building a strongly-coupled component just because it's first or primary purpose makes it seem logical--however unless you have a very good or technical reason to lock it down and hide it from sight then there is little harm in exposing the class so that other components can consume it.
在大型项目中,您会惊讶地发现自己最初构建强耦合组件的频率只是因为它的第一个或主要目的使其看起来合乎逻辑——但是除非您有很好的或技术上的理由将其锁定和隐藏从视觉上看,暴露类以便其他组件可以使用它几乎没有什么害处。
EditKeep in mind that even though we're talking about sub-classes they should be more-or-less well designed and loosely coupled components. Even if they are private and invisible to the outside world keeping a minimal "surface area" between classes will greatly ease the maintainability of your code for future expansion or alteration.
编辑请记住,即使我们在谈论子类,它们也应该或多或少是设计良好的和松散耦合的组件。即使它们是私有的并且对外界不可见,在类之间保持最小的“表面积”也将极大地简化代码的可维护性,以供将来扩展或更改。
回答by Paul Alexander
Generally inner classes should be private and usable only by the class that contains them. If they inner classes are very large that would suggest they should be their own classes.
通常内部类应该是私有的,并且只能由包含它们的类使用。如果它们的内部类非常大,则表明它们应该是它们自己的类。
Usually when you've got a big inner class it's because the inner class is tightly coupled to it's containing class and needs access to its private methods.
通常当你有一个很大的内部类时,这是因为内部类与它的包含类紧密耦合并且需要访问它的私有方法。
回答by Patrik Svensson
I think this is rather subjective, but I would probably split them up in separate code files by making the "host" class partial.
我认为这是相当主观的,但我可能会通过使“主机”类部分化来将它们拆分为单独的代码文件。
By doing like this, you can get even more overview by editing the project fileto make the files group just like designer classes in Windows Forms. I think I've seen a Visual Studio addin that does this automagically for you, but I don't remember where.
通过这样做,您可以通过编辑项目文件使文件组就像 Windows 窗体中的设计器类一样获得更多概览。我想我已经看到一个 Visual Studio 插件可以自动为您执行此操作,但我不记得在哪里。
EDIT:
After some looking I found the Visual Studio add-in for doing this called VSCommands
编辑:
经过一番查找,我找到了用于执行此操作的 Visual Studio 加载项,称为 VSCommands
回答by Tim Robinson
I don't have the book to hand, but the Framework Design Guidelines suggests using public
inner classes as long as clients don't have to refer to the class name. private
inner classes are fine: nobody's going to notice these.
我手头没有这本书,但是框架设计指南建议使用public
内部类,只要客户端不必引用类名。private
内部类很好:没有人会注意到这些。
Bad:ListView.ListViewItemCollection collection = new ListView.ListViewItemCollection();
坏的:ListView.ListViewItemCollection collection = new ListView.ListViewItemCollection();
Good:listView.Items.Add(...);
好的:listView.Items.Add(...);
Regarding your large class: it's generally worthwhile splitting something like this into smaller classes, each with one specific piece of functionality. It's difficult to break it up initially, but I predict it'll make your life easier later on...
关于你的大类:通常值得将这样的东西分成更小的类,每个类都有一个特定的功能。一开始很难打破它,但我预测它会让你以后的生活更轻松......
回答by Reed Copsey
I personally like to have one class per file, and the inner classes as part of that file. I believe inner classes should typically (nearly always) be private, and are an implementation detail of the class. Having them in a separate file confuses things, IMO.
我个人喜欢每个文件有一个类,并将内部类作为该文件的一部分。我相信内部类通常(几乎总是)应该是私有的,并且是类的实现细节。将它们放在一个单独的文件中会使事情变得混乱,IMO。
Using code regions to wrap around the inner classes and hide their details works well for me, in this case, and keeps the file from being difficult to work with. The code regions keep the inner class "hidden", and since it's a private implementation detail, that's fine with me.
在这种情况下,使用代码区域环绕内部类并隐藏它们的细节对我来说效果很好,并且可以防止文件难以使用。代码区域保持内部类“隐藏”,因为它是一个私有的实现细节,所以我没问题。
回答by Mehmet Aras
I personally use inner classes to encapsulate some of concepts and operations used only internally within a class. This way I don't pollute that class' non-public api and keep the api clean and compact.
我个人使用内部类来封装一些仅在类内部使用的概念和操作。这样我就不会污染那个类的非公共 api 并保持 api 干净和紧凑。
You can take advantage of partial classes to move the definition of these inner classes into a different file for better orgnanization. VS does not automatically group partial class files for you except for some of the templatized items such as ASP.NET, WinForm forms, etc. You will need to edit the project file and make some changes in there. You can look at one of the existing grouping in there to see how it is done. I believe there are some macros that allow you to group partial class files for you in the solution explorer.
您可以利用部分类将这些内部类的定义移动到不同的文件中,以便更好地组织。VS 不会自动为您分组部分类文件,除了一些模板化项目,如 ASP.NET、WinForm 表单等。您需要编辑项目文件并在其中进行一些更改。您可以查看其中的现有分组之一,了解它是如何完成的。我相信有一些宏允许您在解决方案资源管理器中为您分组部分类文件。
回答by Robert Paulson
Regarding only how to structure such a beast ...
关于如何构建这样一个野兽......
You can use partial classes to split the main class and the nested classes. When you do so, you're advised to name files appropriately so it's obvious what is going on.
您可以使用分部类来拆分主类和嵌套类。这样做时,建议您适当地命名文件,以便清楚地了解正在发生的事情。
// main class in file Outer.cs
namespace Demo
{
public partial class Outer
{
// Outer class
}
}
// nested class in file Outer.Nested1.cs
namespace Demo
{
public partial class Outer
{
private class Nested1
{
// Nested1 details
}
}
}
In much the same way, you often see (explicit) interfaces in their own file. e.g. Outer.ISomeInterface.cs
rather than the editor default of #region
ing them.
以大致相同的方式,您经常在它们自己的文件中看到(显式)接口。例如,Outer.ISomeInterface.cs
而不是编辑#region
它们的默认设置。
Your projects file structure then starts to look like
您的项目文件结构然后开始看起来像
/Project/Demo/ISomeInterface.cs /Project/Demo/Outer.cs /Project/Demo/Outer.Nested1.cs /Project/Demo/Outer.ISomeInterface.cs
Typically when we're doing this it's for a variation of the Builder pattern.
通常,当我们这样做时,它是针对 Builder 模式的一种变体。
回答by Peter Monks
In my opinion inner classes, if needed at all, should be kept small and used internally by that class only. If you use Relfector on the .NET framework you'll see them used a lot just for that purpose.
在我看来,内部类,如果需要的话,应该保持很小,并且只在该类内部使用。如果您在 .NET 框架上使用 Relfector,您会看到它们被大量用于此目的。
If your inner classes are getting too big I would definitely move them out into separate classes/codefiles somehow if only for maintainability. I have to support some existing code where someone thought it was a great idea to use inner classes within inner classes. It resulted in an inner class hierarchy running 4 - 5 levels deep. Needless to say the code is impenetrable and takes ages to understand what you are looking at.
如果您的内部类变得太大,我肯定会以某种方式将它们移到单独的类/代码文件中,如果只是为了可维护性。我必须支持一些有人认为在内部类中使用内部类是个好主意的现有代码。它导致内部类层次结构运行 4 - 5 层深。毋庸置疑,代码是难以理解的,需要很长时间才能理解你在看什么。
回答by alhpe
Here see a practical example of a nested class that could give you an idea of their use (added some unit test)
这里看到一个嵌套类的实际示例,它可以让您了解它们的使用(添加了一些单元测试)
namespace CoreLib.Helpers
{
using System;
using System.Security.Cryptography;
public static class Rnd
{
private static readonly Random _random = new Random();
public static Random Generator { get { return _random; } }
static Rnd()
{
}
public static class Crypto
{
private static readonly RandomNumberGenerator _highRandom = RandomNumberGenerator.Create();
public static RandomNumberGenerator Generator { get { return _highRandom; } }
static Crypto()
{
}
}
public static UInt32 Next(this RandomNumberGenerator value)
{
var bytes = new byte[4];
value.GetBytes(bytes);
return BitConverter.ToUInt32(bytes, 0);
}
}
}
[TestMethod]
public void Rnd_OnGenerator_UniqueRandomSequence()
{
var rdn1 = Rnd.Generator;
var rdn2 = Rnd.Generator;
var list = new List<Int32>();
var tasks = new Task[10];
for (var i = 0; i < 10; i++)
{
tasks[i] = Task.Factory.StartNew((() =>
{
for (var k = 0; k < 1000; k++)
{
lock (list)
{
list.Add(Rnd.Generator.Next(Int32.MinValue, Int32.MaxValue));
}
}
}));
}
Task.WaitAll(tasks);
var distinct = list.Distinct().ToList();
Assert.AreSame(rdn1, rdn2);
Assert.AreEqual(10000, list.Count);
Assert.AreEqual(list.Count, distinct.Count);
}
[TestMethod]
public void Rnd_OnCryptoGenerator_UniqueRandomSequence()
{
var rdn1 = Rnd.Crypto.Generator;
var rdn2 = Rnd.Crypto.Generator;
var list = new ConcurrentQueue<UInt32>();
var tasks = new Task[10];
for (var i = 0; i < 10; i++)
{
tasks[i] = Task.Factory.StartNew((() =>
{
for (var k = 0; k < 1000; k++)
{
list.Enqueue(Rnd.Crypto.Generator.Next());
}
}));
}
Task.WaitAll(tasks);
var distinct = list.Distinct().ToList();
Assert.AreSame(rdn1, rdn2);
Assert.AreEqual(10000, list.Count);
Assert.AreEqual(list.Count, distinct.Count);
}