为什么/什么时候应该在 .net 中使用嵌套类? 或者你不应该吗?

发布于 2024-07-05 01:09:10 字数 280 浏览 6 评论 0原文

Kathleen Dollard 2008 年的博客文章,她提出了在 .net 中使用嵌套类的一个有趣的原因。 然而,她还提到 FxCop 不喜欢嵌套类。 我假设编写 FxCop 规则的人并不愚蠢,所以这个立场背后一定有推理,但我还没有找到它。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

蓝咒 2024-07-12 01:09:11

基于我对这个概念的理解
当类在概念上彼此相关时,我们可以使用此功能。
我的意思是,其中一些是我们业务中完整的一个项目,就像 DDD 世界中存在的实体一样,有助于聚合根对象完成其业务逻辑。

为了澄清这一点,我将通过一个示例来展示这一点:

假设我们有两个类,例如 Order 和 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();
        }


    }

Base on my understanding of this concept
we could use this feature when classes are related to each other conceptually.
I mean some of them are complete one Item in our business like entities that exist in the DDD world that help to an aggregate root object to complete its business logic.

In order to clarify I'm going to show this via an example:

Imagine that we have two classes like Order and OrderItem.
In order class, we are going to manage all orderItems and in OrderItem we are holding data about a single order
for clarification, you can see below classes:

 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();
        }


    }
安稳善良 2024-07-12 01:09:11

对于这种情况是的:

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);
        }
    }
}

yes for this case:

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);
        }
    }
}
愿与i 2024-07-12 01:09:11

请记住,您需要测试嵌套类。 如果它是私有的,您将无法单独测试它。

不过,您可以将其设置为内部,InternalsVisibleTo 属性结合使用。 然而,这与仅出于测试目的而将私有字段设置为内部相同,我认为这是糟糕的自我文档。

因此,您可能只想实现复杂度较低的私有嵌套类。

Bear in mind that you'll need to test the nested class. If it is private, you won't be able to test it in isolation.

You could make it internal, though, in conjunction with the InternalsVisibleTo attribute. However, this would be the same as making a private field internal only for testing purposes, which I consider bad self-documentation.

So, you may want to only implement private nested classes involving low complexity.

霓裳挽歌倾城醉 2024-07-12 01:09:11

正如 nawfal 提到的抽象工厂模式的实现,可以扩展该代码来实现 类簇模式基于抽象工厂模式。

As nawfal mentioned implementation of Abstract Factory pattern, that code can be axtended to achieve Class Clusters pattern which is based on Abstract Factory pattern.

灵芸 2024-07-12 01:09:11

我喜欢嵌套单个类特有的异常,即。 那些从未从其他地方扔过的东西。

例如:

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()
    }
}

这有助于保持项目文件整洁,并且不会充满一百个粗短的小异常类。

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.

你的他你的她 2024-07-12 01:09:11

嵌套类可以用于以下需求:

  1. 数据分类
  2. 当主类的逻辑很复杂并且您感觉需要从属对象来管理该类时
  3. 当您认为类的状态和存在完全依赖于外围对象时班级

The nested classes can be used for following needs:

  1. Classification of the data
  2. When the logic of the main class is complicated and you feel like you require subordinate objects to manage the class
  3. When you that the state and existence of the class fully depends on the enclosing class
平安喜乐 2024-07-12 01:09:11

嵌套类的另一个尚未提及的用途是泛型类型的隔离。 例如,假设想要拥有一些静态类的通用系列,这些静态类可以采用具有不同数量参数的方法以及其中一些参数的值,并生成具有较少参数的委托。 例如,人们希望有一个静态方法,它可以采用 Action 并生成 String ,该方法将调用提供的操作将 3.5 作为 double 传递; 人们可能还希望有一个静态方法,它可以接受一个 Action 并产生一个 Action,传递 7< /code> 作为 int5.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);

使用嵌套泛型类型可以判断哪些委托适用于总体类型描述的哪些部分。

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 7 as the int and 5.3 as the double. Using generic nested classes, one can arrange to have the method invocations be something like:

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.

清旖 2024-07-12 01:09:11

如果我理解凯瑟琳的文章正确的话,她建议使用嵌套类来编写 SomeEntity.Collection 而不是 EntityCollection< 某个实体>。 在我看来,这是一种有争议的方式来节省你的打字时间。 我非常确定在现实世界中应用程序集合在实现上会有一些差异,因此无论如何您都需要创建单独的类。 我认为使用类名来限制其他类的范围不是一个好主意。 它污染了智能感知并加强了类之间的依赖关系。 使用命名空间是控制类范围的标准方法。 然而,我发现像 @hazzen 评论中那样使用嵌套类是可以接受的,除非你有大量的嵌套类,这是糟糕设计的标志。

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.

小清晰的声音 2024-07-12 01:09:11

我经常使用嵌套类来隐藏实现细节。 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);

Also I can have a generic list of Equality但不是 EqualityComparer

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
}

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 question for 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>

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
}
茶底世界 2024-07-12 01:09:11

来自 Sun 的 Java 教程:

为什么使用嵌套类?
使用嵌套类有几个令人信服的理由,其中包括:

  • 它是一种对仅在一个地方使用的类进行逻辑分组的方法。
  • 它增加了封装。
  • 嵌套类可以产生更具可读性和可维护性的代码。

类的逻辑分组——如果一个类仅对另一个类有用,那么将其嵌入到该类中并将两个类放在一起是合乎逻辑的。 嵌套这样的“帮助类”使得它们的包更加精简。

增强封装性 - 考虑两个顶级类 A 和 B,其中 B 需要访问 A 的成员,否则这些成员将被声明为私有。 通过将类 B 隐藏在类 A 中,可以将 A 的成员声明为私有,并且 B 可以访问它们。 另外,B本身可以对外界隐藏。 <- 这不适用于C#的嵌套类实现,这只适用于Java。

更具可读性、可维护性的代码——在顶级类中嵌套小类使代码更接近其使用位置。

From Sun's Java Tutorial:

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.

More readable, maintainable code—Nesting small classes within top-level classes places the code closer to where it is used.

樱桃奶球 2024-07-12 01:09:11

完全惰性且线程安全的单例模式

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();
    }
}

源:https://csharpin深度.com/Articles/Singleton

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();
    }
}

source: https://csharpindepth.com/Articles/Singleton

指尖凝香 2024-07-12 01:09:11

这取决于用途。 我很少使用公共嵌套类,但一直使用私有嵌套类。 私有嵌套类可用于仅在父对象内部使用的子对象。 例如,HashTable 类包含一个私有 Entry 对象以仅在内部存储数据。

如果该类旨在由调用者(外部)使用,我通常喜欢将其作为单独的独立类。

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.

If the class is meant to be used by the caller (externally), I generally like making it a separate standalone class.

绮筵 2024-07-12 01:09:11

除了上面列出的其他原因之外,我还能想到的另一个原因不仅是使用嵌套类,而且实际上是公共嵌套类。 对于那些使用共享相同泛型类型参数的多个泛型类的人来说,声明泛型命名空间的能力将非常有用。 不幸的是,.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 个或更多类型参数的“通用命名空间”。 必须使这些类型参数在所有需要知道类型参数的九种类型之间保持同步是乏味的,尤其是在添加新参数时。 通用命名空间的使用使代码更易于管理和可读。

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:

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.

For example:

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);
    }

}

The files in the visual studio solution explorer would then be organized as such:

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

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 {}

}

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.

陌生 2024-07-12 01:09:10

当您嵌套的类仅对封闭类有用时,请使用嵌套类。 例如,嵌套类允许您编写类似(简化的)的内容:

public class SortedMap {
    private class TreeNode {
        TreeNode left;
        TreeNode right;
    }
}

您可以在一个地方对您的类进行完整的定义,您不必跳过任何 PIMPL 环来定义您的类如何工作以及外部世界不需要看到你的任何实现。

如果 TreeNode 类是外部的,您要么必须将所有字段设为 public ,要么创建一堆 get/set 方法才能使用它。 外界会有另一个类污染他们的智能感知。

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.

If the TreeNode class was external, you would either have to make all the fields public or make a bunch of get/set methods to use it. The outside world would have another class polluting their intellisense.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文