为何/何时应使用嵌套的课程。净?或者不应该吗?
题
在 凯瑟琳*多拉德最近的博客, 她提出了一个有趣的理由使用嵌套的课程。网。然而,她还提到,FxCop不喜欢套课程。我假设,人们的写作FxCop规则不是愚蠢,因此必须有理由这一立场,但我没有能够找到它。
解决方案
使用嵌套级上课的时候你是嵌套是唯一有用的封闭类。例如,课程的嵌套能让你写的东西像(简化):
public class SortedMap {
private class TreeNode {
TreeNode left;
TreeNode right;
}
}
你可以做一个完整的定义类在一个地方,你没有跳过任何PIMPL篮球的定义如何类工作,与外部世界不需要什么都看不见你的实施。
如果树节点类是外部的,你要么必须使所有的领域 public
或者让一堆 get/set
方法的使用它。外面的世界将会有另一类污染他们的智能感知。
其他提示
为什么使用嵌套的课?有几个令人信服的理由对于使用嵌套的课程,其中包括:
- 它是一种在逻辑上分组的课程,这些课程只用于一个地方。
- 它增加了封装。
- 嵌套课程可导致更易读和易于维护的代码。
逻辑分组类(如果一类是有用的唯一一个其他类,那么它是合乎逻辑的嵌入它在那类和保持两个在一起。筑巢的这种"辅助课程",使他们的软件包的更加精简。
增加的封装—考虑两个顶级课程,A和B,B需要访问的成员,否则将被宣布为私有。通过隐藏B类内一级,成员国可以声明的私人和B可以对它们进行访问。此外,B本身可以被隐藏在外面的世界。 <-这不适用于C#'s执行情况的嵌套的课程,这只适用于Java。
更具可读性,维护的代码—筑巢的小类内的顶级课程的地方代码接近它在哪里使用。
完全懒惰和线安全的单独模式
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();
}
}
它取决于使用。我很少会使用公共嵌套类,但使用嵌套的私人类所有的时间。私人嵌套类可用于分对象,目的是只能用内部的父母。这方面的一个例子是,如果一个HashTable类包含一个私人项目,以储存数据的境内只。
如果该类意味着要使用通过调用者(外),我通常喜欢使它成为一个独立独立的类。
除了其他原因,上面列出的,还有一个原因,我能想到的不仅是使用嵌套的课程,但实际上公共嵌套课程。对于那些工作与多个通用课程,共享相同的一般类型参数,能够宣布一个通用名称空间将非常有用的。不幸的是,.网(或至少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>
{
}
我们可以简化签名的这些类通过使用通用的名字空间(通过实施嵌套类):
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 {}
}
然后,通过使用分类的建议,由埃里克*范*布拉克尔音乐在一个较早的意见,可以分类为单独的套文件。我建议使用Visual Studio扩展喜欢巢支持的筑巢的分类文件。这允许的"空间"类文件还被用来组织的嵌套类文件夹中的文件喜欢的方式。
例如:
实体。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
{
}
实体。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;
}
}
}
实体。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;
}
}
}
实体。IBaseBusiness.cs
partial class Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
public interface IBaseBusiness
{
tDataObjectList Load();
void Delete();
void Save(tDataObjectList data);
}
}
实体。IBaseDataAccess.cs
partial class Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
public interface IBaseDataAccess
{
tDataObjectList Load();
void Delete();
void Save(tDataObjectList data);
}
}
该文件在visual studio解决方案资源管理器,然后将举办这样的:
Entity.cs
+ Entity.BaseDataObject.cs
+ Entity.BaseDataObjectList.cs
+ Entity.IBaseBusiness.cs
+ Entity.IBaseDataAccess.cs
你将执行的通用名称空间如下:
用户。cs
public
partial class User
:
Entity
<
User.DataObject,
User.DataObjectList,
User.IBusiness,
User.IDataAccess
>
{
}
用户。数据对象.cs
partial class User
{
public class DataObject : BaseDataObject
{
public string UserName { get; set; }
public byte[] PasswordHash { get; set; }
public bool AccountIsEnabled { get; set; }
}
}
用户。DataObjectList.cs
partial class User
{
public class DataObjectList : BaseDataObjectList {}
}
用户。汇网.cs
partial class User
{
public interface IBusiness : IBaseBusiness {}
}
用户。IDataAccess.cs
partial class User
{
public interface IDataAccess : IBaseDataAccess {}
}
和文件将是有组织的方案资源管理器如下:
User.cs
+ User.DataObject.cs
+ User.DataObjectList.cs
+ User.IBusiness.cs
+ User.IDataAccess.cs
上述是一个简单的例子使用的外流作为一个通用名称空间。我已经建立"通用名称空间"的含9个或更多类型参数的过去。具有保持这些类型参数同步的九种类型,所需要知道的类型参数繁琐的,尤其是当添加一个新的参数。使用的通用名称空间使,代码远更易于管理和可读性。
如果我的理解Katheleen的文章,她建议使用嵌套类能够写SomeEntity.集合,而不是EntityCollection< SomeEntity>.在我看来,这是有争议的方式以节省一些输入。我敢肯定,在现实世界的应用程序的集会有一些差异实现,所以你会需要建立单独的类。我认为,使用类名来限制其他类范围不是一个好主意。它污染智能感知和加强之间的依赖关系课程。使用名称空间是一个标准的方式控制课程的范围。但是我发现的使用嵌套类似于@hazzen意见是可以接受的,除非你有大量的嵌套的课程,这是一个迹象的糟糕的设计。
另一种使用没有提到为课程的嵌套是分离的一般类型。例如,假如一个人想要有一些一般的家庭的静态的课程,可以采取的方法与各种数量的参数,以及与数值对于这些参数,以及产生的代表与少参数。例如,一个希望有一个静态的方法,该方法可以把一个 Action<string, int, double>
并产生一个 String<string, int>
它将呼吁所提供的动作传递3.5作为 double
;一个可能也希望有一个静态的方法,它可取一个 Action<string, int, double>
并产生一个 Action<string>
, 传递 7
作为 int
和 5.3
作为 double
.使用嵌套通用类别,可以安排的方法调用可以是这样的:
MakeDelegate<string,int>.WithParams<double>(theDelegate, 3.5);
MakeDelegate<string>.WithParams<int,double>(theDelegate, 7, 5.3);
或者,因为后一种类型在每一个表中可以推断出,即使以前的人不能:
MakeDelegate<string,int>.WithParams(theDelegate, 3.5);
MakeDelegate<string>.WithParams(theDelegate, 7, 5.3);
使用嵌套通用类型,使得有可能告诉这位代表都适用于这部分的总体类型的描述。
这套课程可以用于以下需要:
- 分类的数据
- 当逻辑的主要类是复杂的和你觉得你需要从属物管理类
- 当你该国和存在类完全取决于封闭类
我经常使用嵌套类隐藏的执行情况的详细说明。 一个例子从埃里克利珀特的答案在这里:
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 { ... }
}
这种模式变得更有用的仿制药。 看看 这个问题 两个很酷的实例。所以我最后写
Equality<Person>.CreateComparer(p => p.Id);
而不是的
new EqualityComparer<Person, int>(p => p.Id);
我也可以有一个通用名单 Equality<Person>
但不是 EqualityComparer<Person, int>
var l = new List<Equality<Person>>
{
Equality<Person>.CreateComparer(p => p.Id),
Equality<Person>.CreateComparer(p => p.Name)
}
作为在那里
var l = new List<EqualityComparer<Person, ??>>>
{
new EqualityComparer<Person, int>>(p => p.Id),
new EqualityComparer<Person, string>>(p => p.Name)
}
是不可能的。这是有益的嵌套类继承父类。
另一种情况(同样性质的-隐藏实施)是当你想要做一类的成员(领域,性等)只能为一个单一的等级:
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
}
我喜欢鸟巢的例外情况是独一个类,即。那些从来不扔任何其他地方。
例如:
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()
}
}
这有助于保持项目文件整理和未充分的百末节的小异常类。
记住,你只需要测试的嵌套类。如果是私人的,你不可以测试它在隔离。
你可以做到这一点内部,虽然, 在结合 InternalsVisibleTo
属性.然而,这将是相同的,因为使私人领域内只有为测试目的,我认为坏自文件。
所以,你可能想到的唯一实行私有嵌套课程涉及低复杂性。
是这样的情况:
class Join_Operator
{
class Departamento
{
public int idDepto { get; set; }
public string nombreDepto { get; set; }
}
class Empleado
{
public int idDepto { get; set; }
public string nombreEmpleado { get; set; }
}
public void JoinTables()
{
List<Departamento> departamentos = new List<Departamento>();
departamentos.Add(new Departamento { idDepto = 1, nombreDepto = "Arquitectura" });
departamentos.Add(new Departamento { idDepto = 2, nombreDepto = "Programación" });
List<Empleado> empleados = new List<Empleado>();
empleados.Add(new Empleado { idDepto = 1, nombreEmpleado = "John Doe." });
empleados.Add(new Empleado { idDepto = 2, nombreEmpleado = "Jim Bell" });
var joinList = (from e in empleados
join d in departamentos on
e.idDepto equals d.idDepto
select new
{
nombreEmpleado = e.nombreEmpleado,
nombreDepto = d.nombreDepto
});
foreach (var dato in joinList)
{
Console.WriteLine("{0} es empleado del departamento de {1}", dato.nombreEmpleado, dato.nombreDepto);
}
}
}