在Kathleen Dollard的2008年博客文章中,她提出了一个有趣的理由在.net中使用嵌套类。但是,她还提到FxCop不喜欢嵌套类。我假设编写FxCop规则的人并不愚蠢,因此在该职位后面必须有推理,但我找不到它。
在Kathleen Dollard的2008年博客文章中,她提出了一个有趣的理由在.net中使用嵌套类。但是,她还提到FxCop不喜欢嵌套类。我假设编写FxCop规则的人并不愚蠢,因此在该职位后面必须有推理,但我找不到它。
Answers:
当您要嵌套的类仅对封闭类有用时,请使用嵌套类。例如,嵌套类允许您编写类似(简化)的内容:
public class SortedMap {
private class TreeNode {
TreeNode left;
TreeNode right;
}
}
您可以在一个地方对类进行完整定义,而不必跳过任何PIMPL箍来定义类的工作方式,并且外界无需查看任何实现。
如果TreeNode类是外部类,则必须创建所有字段public
或get/set
使用大量方法来使用它。外部世界将有另一类污染他们的智力。
为什么要使用嵌套类?使用嵌套类有许多令人信服的原因,其中包括:
类的逻辑分组-如果一个类仅对另一个类有用,则将其嵌入该类并将两者保持在一起是合乎逻辑的。嵌套此类“帮助程序类”可使它们的程序包更加简化。
增加的封装-考虑两个顶级类A和B,其中B需要访问A的成员,否则将其声明为私有。通过将类B隐藏在类A中,可以将A的成员声明为私有,而B可以访问它们。另外,B本身可以对外界隐藏。<-这不适用于C#的嵌套类的实现,仅适用于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();
}
}
除了上面列出的其他原因外,还有一个原因使我不仅想到使用嵌套类,而且实际上可以使用公共嵌套类。对于那些使用共享相同泛型类型参数的多个泛型类的人来说,声明泛型名称空间的能力将非常有用。不幸的是,.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>
{
}
我们可以使用通用名称空间(通过嵌套类实现)来简化这些类的签名:
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 {}
}
然后,通过使用Erik van Brakel在先前的注释中建议的局部类,可以将这些类分离为单独的嵌套文件。我建议使用像NestIn这样的Visual Studio扩展来支持嵌套部分类文件。这允许“命名空间”类文件也可用于以类似方式在文件夹中组织嵌套的类文件。
例如:
Entity.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
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
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
partial class Entity<tDataObject, tDataObjectList, tBusiness, tDataAccess>
{
public interface IBaseBusiness
{
tDataObjectList Load();
void Delete();
void Save(tDataObjectList data);
}
}
Entity.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
然后您将实现通用名称空间,如下所示:
User.cs
public
partial class User
:
Entity
<
User.DataObject,
User.DataObjectList,
User.IBusiness,
User.IDataAccess
>
{
}
User.DataObject.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
partial class User
{
public class DataObjectList : BaseDataObjectList {}
}
User.IBusiness.cs
partial class User
{
public interface IBusiness : IBaseBusiness {}
}
User.IDataAccess.cs
partial class User
{
public interface IDataAccess : IBaseDataAccess {}
}
这些文件将在解决方案资源管理器中进行组织,如下所示:
User.cs
+ User.DataObject.cs
+ User.DataObjectList.cs
+ User.IBusiness.cs
+ User.IDataAccess.cs
上面是使用外部类作为通用名称空间的简单示例。我过去建立了包含9个或更多类型参数的“通用名称空间”。必须使所有类型类型的参数在所有九种类型信息之间保持同步是很繁琐的,尤其是在添加新参数时。通用名称空间的使用使该代码更具可管理性和可读性。
我经常使用嵌套类来隐藏实现细节。埃里克·利珀特(Eric Lippert)的答案示例如下:
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
}
嵌套类尚未提及的另一个用途是泛型类型的隔离。例如,假设有人希望拥有一些静态类的通用类,这些类可以采用带有各种参数的方法以及其中一些参数的值,并生成带有较少参数的委托。例如,一个人希望有一个静态方法,该方法可以采用an Action<string, int, double>
并产生a 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);
使用嵌套的泛型类型可以分辨出哪些委托适用于整个类型描述的哪些部分。
嵌套类可用于以下需求:
我喜欢嵌套单个类唯一的异常,即。从来没有从其他地方扔过的东西。
例如:
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()
}
}
这有助于使您的项目文件保持整洁,并且不会充满一百个粗俗的小异常类。
是的,在这种情况下:
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);
}
}
}
根据我对这个概念的理解,当类在概念上相互关联时,我们可以使用此功能。我的意思是,其中有些像DDD世界中存在的实体一样是我们业务中完整的一项,可帮助聚合根对象完成其业务逻辑。
为了澄清起见,我将通过一个示例来展示这一点:
想象一下,我们有两个类,例如Order和OrderItem。在订单类中,我们将管理所有orderItems,在OrderItem中,我们保留有关单个订单的数据以进行澄清,您可以看到以下类:
class Order
{
private List<OrderItem> _orderItems = new List<OrderItem>();
public void AddOrderItem(OrderItem line)
{
_orderItems.Add(line);
}
public double OrderTotal()
{
double total = 0;
foreach (OrderItem item in _orderItems)
{
total += item.TotalPrice();
}
return total;
}
// Nested class
public class OrderItem
{
public int ProductId { get; set; }
public int Quantity { get; set; }
public double Price { get; set; }
public double TotalPrice() => Price * Quantity;
}
}
class Program
{
static void Main(string[] args)
{
Order order = new Order();
Order.OrderItem orderItem1 = new Order.OrderItem();
orderItem1.ProductId = 1;
orderItem1.Quantity = 5;
orderItem1.Price = 1.99;
order.AddOrderItem(orderItem1);
Order.OrderItem orderItem2 = new Order.OrderItem();
orderItem2.ProductId = 2;
orderItem2.Quantity = 12;
orderItem2.Price = 0.35;
order.AddOrderItem(orderItem2);
Console.WriteLine(order.OrderTotal());
ReadLine();
}
}