Generics in .Net – Part 5 – Generic Namespaces

So, it’s been awhile since I’ve published a post on this site.  Today I found myself answering a stack overflow post with essentially what I had planned on covering for this part of my series on generics in .net.  So I figured that I would post the same content here.  As a result, this 5th part is being publish out of order and before parts 2-4.  Hopefully, it won’t be another year before I get around to posting those parts.  So with that said, here is the post.  Note that some of the concepts employed in this post will be covered in parts 2-4.

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 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.