Parametric or generic namespaces are a specialization of parametric polymorphism where type parameters are defined on an outer construct that itself contains types that share access to the type parameters.
Wikipedia defines a namespace as:
In computing, a namespace is a set of symbols that are used to organize objects of various kinds, so that these objects may be referred to by name.
Concept:
Parametric or generic namespaces are containers where by types defined within the container share visibility and access to type parameters for which arguments will be supplied at a later time. Typically, these types are logically related and define a package of related abstract functionality. These types if defined upon their own, would repeatedly define the same generic type parameters resulting in code that is potentially more unwieldy, difficult to maintain and less DRY.
Example:
Consider the following related types for example:
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>
{
}
The code for the types can be simplified and DRYed up by leveraging a parametric namespace. For example:
namespace 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 {}
}
And then the parametric namespace and its contained types may be derived like this:
namespace User
:
Entity
<
User.DataObject,
User.DataObjectList,
User.IBusiness,
User.IDataAccess
>
{
public class DataObject : BaseDataObject {}
public class DataObjectList : BaseDataObjectList {}
public interface IBusiness : IBaseBusiness {}
public interface IDataAccess : IBaseDataAccess {}
}
namespace Article
:
Entity
<
Article.DataObject,
Article.DataObjectList,
Article.IBusiness,
Article.IDataAccess
>
{
public class DataObject : BaseDataObject {}
public class DataObjectList : BaseDataObjectList {}
public interface IBusiness : IBaseBusiness {}
public interface IDataAccess : IBaseDataAccess {}
}
Types from the specialized namespaces are then accessed as such:
public class Program
{
static void Demo(User.IBusiness userBusiness, Article.IBusiness articleBusiness)
{
var users = new User.DataObjectList();
var user = new User.DataObject();
var articles = new Articles.DataObjectList();
var article = new Articles.DataObject();
}
}
Benefits:
The benefits of the parametric namespace are as follows:
- Defines each type parameter only once across several related types;
- New type parameters are immediately visible across all contained types;
- Neatly packages parametric abstract functionality in the same way that classical namespaces provide, but with the added benefit of only having to specify type arguments once;
- Provides basic parametric polymorphism across an array of related types
- Provides and promotes uniformity of the contained type names across specialized derived namespaces.
Support:
As of yet, there is very little if any support for this concept in mainstream languages. However, this organization concept may still be possible to accomplish by way of a combination of parametric classes or interfaces and nested types. For example, in C#, you can define generic classes with type parameters that are then visible across all nested types contained within the generic class.
Questions and answers regarding the sharing of generic parameters across multiple types should have this tag applied.
Relevant links: