Refactoring classes into multi-layered generic classes in C# - c#

I have a problem with C# generics, and I'm not sure about the most elegant solution. I've been programming a while but am new to the C# ecosystem so don't know common terminology for searching.
I'm trying to refactor code to reduce existing copy-paste duplication of classes. It is easy to resolve with one level of generics, but I can't get it working with two.
A very simplified example is below. The core issue is that BaseProfile cannot use any implementation details relating to DetailsA or DetailsB as it does not know the type. So UpdateDetailsId() has to be duplicated in 2 derived classes, instead of having a single Profile class handle it. Keep in mind this is a toy example just to express the relationships. The real classes have tens of fields, but a common subset which we are using in the class in question, so even if DetailsA and DetailsB look identical assume we need both.
public abstract class BaseProfile<TypeOfPerson>
{
public TypeOfPerson Person { get; set; }
}
public class Profile1 : BaseProfile<PersonA>
{
public void UpdateDetailsId(int id)
{
this.Person.Details.Id = id;
}
}
public class Profile2 : BaseProfile<PersonB>
{
public void UpdateDetailsId(int id)
{
this.Person.Details.Id = id;
}
}
public class PersonA
{
public DetailsA Details { get; set; }
}
public class PersonB
{
public DetailsB Details { get; set; }
}
public class DetailsA
{
public int Id { get; set; }
}
public class DetailsB
{
public int Id { get; set; }
}
I can add interfaces as it is referring to all the same fields for each type. However, C# will not allow an interface to include another interface and automatically resolve it in the implementation, because the member has to exactly match i.e. I thought I could just add IDetails Details to the IPerson interface but the fields now need to be type IDetails instead of DetailsA which implements IDetails. If I do that then I lose compiler type safety and can put the wrong Details on the wrong Person.
I have had success doing a public/private field pair like below, but this only validates and throws at runtime when casting value to DetailsA. I'd prefer something safer but I don't know if this is the best option. The goal of this example is a single Profile class, handling multiple Person classes, each with their own Details type that has an int Id field.
public class PersonA : IPerson
{
public IDetails Details
{
get { return _details; }
set { _details = (DetailsA)value; }
}
private DetailsA _details { get; set; }
}

One way of achieving this is by defining the type relationship between PersonA to DetailsA in a generic way, and specify a second generic type on BaseProfile.
Profile1 : BaseProfile<PersonA, DetailsA>
Consider the following code (note that I'm using Net6, so I have all these nullable reference type operators):
public abstract class BaseProfile<TPerson, TDetails>
where TDetails : IDetails, new()
where TPerson : PersonDetails<TDetails>, new()
{
public TPerson? Person { get; set; } = new TPerson();
public virtual void UpdateDetailsId(int id)
{
Person!.Details!.Id = id;
}
}
public class Profile1 : BaseProfile<PersonA, DetailsA>
{
}
public class Profile2 : BaseProfile<PersonB, DetailsB>
{
}
public abstract class PersonDetails<TDetails>
where TDetails : IDetails, new()
{
public virtual TDetails? Details { get; set; } = new TDetails();
}
public class PersonA : PersonDetails<DetailsA>
{
}
public class PersonB : PersonDetails<DetailsB>
{
}
public interface IDetails
{
int Id { get; set; }
}
public class DetailsA : IDetails
{
public int Id { get; set; }
public string? FirstName { get; set; }
}
public class DetailsB : IDetails
{
public int Id { get; set; }
public string? LastName { get; set; }
}
Testing with the following snippet
var profile1 = new Profile1();
var profile2 = new Profile2();
profile1.UpdateDetailsId(10);
profile2.UpdateDetailsId(12);
Console.WriteLine(profile1.Person!.Details!.Id);
Console.WriteLine(profile2.Person!.Details!.Id);
Console.WriteLine();
Update:
Because you included explicit casting in your snippet for Details property getters and setter, I also want to show a pattern using a concrete type inheriting on these generic types -- then demonstrate implicit/explicit operator user-defined conversion patterns.
Add the following declarations:
public abstract class BaseProfile<TPerson>
where TPerson : PersonDetails<GenericDetails>, new()
{
public TPerson? Person { get; set; } = new TPerson();
public virtual void UpdateDetailsId(int id)
{
Person!.Details!.Id = id;
}
public static explicit operator Profile1(BaseProfile<TPerson> details)
{
var profile = new Profile1();
profile.Person!.Details = (GenericDetails)details.Person!.Details!;
return profile;
}
public static explicit operator Profile2(BaseProfile<TPerson> details)
{
var profile = new Profile2();
profile.Person!.Details = (GenericDetails)details.Person!.Details!;
return profile;
}
}
public class GenericProfile : BaseProfile<GenericPerson>
{
}
public abstract class GenericPersonDetails : PersonDetails<GenericDetails>
{
}
public class GenericPerson : GenericPersonDetails
{
}
public class GenericDetails : IDetails
{
public int Id { get; set; }
public static implicit operator DetailsA(GenericDetails details)
{
return new DetailsA() { Id = details.Id };
}
public static implicit operator DetailsB(GenericDetails details)
{
return new DetailsB() { Id = details.Id };
}
}
and, update the testing functional scope:
var profile1 = new Profile1();
var profile2 = new Profile2();
var genericProfile = new GenericProfile();
profile1.UpdateDetailsId(10);
profile2.UpdateDetailsId(12);
genericProfile.UpdateDetailsId(20);
Console.WriteLine(profile1.Person!.Details!.Id);
Console.WriteLine(profile1.Person!.Details!.FirstName ?? "No First Name");
Console.WriteLine(profile2.Person!.Details!.Id);
Console.WriteLine(profile2.Person!.Details!.LastName ?? "No Last Name");
Console.WriteLine(genericProfile.Person!.Details!.Id);
Console.WriteLine(((Profile1)genericProfile).Person!.Details!.FirstName ?? "No First Name");
Console.WriteLine(((Profile2)genericProfile).Person!.Details!.LastName ?? "No Last Name");
Console.WriteLine();

Related

How can I write a class that is both inheritable and generic?

I have several classes in my project that are mapped to table rows in a database.
Most of the classes share a lot of the boiler plate code to fetch data from database FetchAll, and to return certain object/row based on a given id FetchById().
I am thinking of writing a single class that can perform FetchAll and FetchById() and then all of my other classes can inherit from this object. However, I am struggling to define the base class to be generic. Other classes can't inherit of it and at the same time be of the generic type associated with what they are inheriting from.
To better explain my question, I will start simplifying two of my common classes that share similar code:
Person.cs
class Person {
public int Id { get; set; }
public string Name { get; set; }
public static List<Person> All {
get { /* fetch all rows and */ return List<Person>; }
}
public static Person FetchById(int id) {
return All.Where(p => p.Id == id).SingleOrDefault();
}
}
Department.cs
class Department {
public int Id { get; set; }
public string Name { get; set; }
public string CostCenter { get; set; }
public static List<Department> All { get { /* same logic as with Person */ } }
public static Department FetchById(int id) { /* same logic as with Person */ }
}
Now here is what I tried to do to reduce code duplication:
IIdentifiable.cs
public interface IIdentifiable {
// make sure all objects have an Id property
int Id { get; set; }
}
DbObject.cs
class DbObject<T> where T: IIdentifiable {
public static List<T> All { get { /* return List<T>; */ } }
public static T FetchById(int id) {
return All.Where(object => object.Id == id).FirstOrDefault();
}
}
Now I went back to my Person and Department classes to take advantage of DbObject:
Person.cs (modified)
class Person : DbObject<Person>, IIdentifiable // won't compile: The type cannot be used as type parameter T
{
public int Id { get; set; }
public string Name { get; set; }
// public static List<Person> All {get{ ... }}; // should be inherited
// public static Person FetchById(int id) { ... } // should be inherited
}
I did the same with Department. You get the idea.
I feel I am doing something wrong. I know it should be much simpler than this.
How can I make DbObject generic and inheritable at the same time?
You can do this by adopting the Curiously recurring template pattern.
Effectively you define DbObject<T> such that T : DbObject<T> then it works.
public class DbObject<T> where T : DbObject<T>, IIdentifiable
{
public List<T> All { get { return new List<T>(); } }
public T FetchById(int id)
{
return All.Where(x => x.Id == id).FirstOrDefault();
}
}
public class Person : DbObject<Person>, IIdentifiable
{
public int Id { get; set; }
public string Name { get; set; }
}
Now this code works:
Person p = new Person();
List<Person> all = p.All;
In your original code you were using static methods. I assume that is so you could write List<Person> all = Person.All;, but you can't inherit static methods.
You might want to consider making a repository object that is responsible for returning the people.
Do keep in mind that the downside to this approach is that you need to be careful. This code compiles, but would probably not be what you want:
public class Department : DbObject<Person>, IIdentifiable
{
public int Id { get; set; }
public string Name { get; set; }
}
You can use recursive generics and static methods.
public abstract class Identified<T> where T : Identified<T>{
public static List<T> GetAll(){...}
}
public class Person:Identified<Person>{...}
var allPeople = Person.GetAll();
You shouldn't need to redefine GetAll in each subtype. But I would recommend that FetchById performs a separate query. Do you really want to load the entire table to view one record?
But I would actually recommend a separate generic factory class. It's difficult to configure and test static methods.

Generic Attributes in MVC models

I created the GenericAttribute.cs file in my Models
public class GenericAttributes<T>
{
public T Id { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedDate { get; set; }
}
Now I want to add 'int id' field in my User Model
public class User
{
//here I want to add 'Id' field
public string UserId { get; set; }
public string password { get; set; }
public string UserType { get; set; }
public int EmployeeId { get; set; }
public virtual Employee employee { get; set; }
}
How should I do this? Please help
You can make GenericAttributes an interface so you can implement it where ever.
Such as;
public interface IGenericAttributes<T>
{
//properties
}
And use in your class declaration;
public class User : IGenericAttributes<int>
{
//properties
}
This will force your concrete type User to implement the properties of the interface.
You are getting some conflicting answers due to your naming convention. Any class of the form xxxAttribute is expected to be a subclass of the Attribute class. Attributes are metadata that you can attach to classes, fields, etc. Using reflection you can read these attributes, which is a powerful way to inform various APIs about how to interact with your custom classes - without inheritance or an interface.
If this sort of metadata is your intent, then Barr J's answer is correct. However, if your intent is for the GenericAttributes class to serve as a base class that you can inherit these properties from, then Tom Johnson is correct (although he did change GenericAttributes into an interface instead of a base class, but same result if all you have are properties like this). The latter is most likely what you are looking for.
I would suggest renaming GenericAttributes to something more descriptive, like BaseRecord or IRecord (as an interface), since User looks like data coming from or going to a database.
It would also be handy to have a non-generic version of the class/interface so that you can non-generically reference such records.
public class BaseRecord {
public Type IdType { get; }
private Object _id = null;
public Object Id {
get {
return _id;
}
set {
if(value != null) {
if(!IdType.IsAssignableFrom(value.GetType()))
throw new Exception("IdType mismatch");
}
_id = value;
}
}
public bool IsActive { get; set; }
public DateTime CreatedTime { get; set; }
public BaseRecord(Type idType)
{
if(idType == null) throw new ArgumentNullException("idType");
this.IdType = idType;
}
}
namespace Generic {
public class BaseRecord<T> : BaseRecord
{
new public T Id {
get { return (T)base.Id; }
set { base.Id = value; }
}
public BaseRecord() : base(typeof(T))
{
}
}
}
public class User : Generic.BaseRecord<int>
{}
public class OtherRecord : Generic.BaseRecord<string>
{}
// This inheritence scheme gives you the flexibility to non-generically reference record objects
// which can't be done if you only have generic base classes
BaseRecord r = new User();
r = new OtherRecord();
BaseRecord records[] = { new User(), new OtherRecord() };
To access the id for GenericAttributes class, you'll have to cast User object as base class type.
namespace SampleApp
{
class SampleProgram
{
static void Main(string[] args)
{
User User = new User() { Id = 1 };
var genericAttribute = (User as GenericAttributes<int>);
genericAttribute.Id = 2;
var genericAttributeId = genericAttribute.Id;
var classId = User.Id;
}
}
public class GenericAttributes<T>
{
public T Id { get; set; }
}
public class User : GenericAttributes<int>
{
public new int Id { get; set; }
}
}

Cast concrete type to nested generic base type

Let's say I have nested generic data classes similar to the following:
public class BaseRecordList<TRecord, TUserInfo>
where TRecord : BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual IList<TRecord> Records { get; set; }
public virtual int Limit { get; set; }
}
public class BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
public class BaseUserInfo
{
public virtual string Name { get; set; }
public virtual int Age { get; set; }
}
With 2 concrete versions like so:
// Project 1: Requires some extra properties
public class Project1RecordList : BaseRecordList<Project1Record, Project1UserInfo> {}
public class Project1Record : BaseRecord<Project1UserInfo>
{
public Guid Version { get; set; }
}
public class Project1UserInfo : BaseUserInfo
{
public string FavouriteFood { get; set; }
}
and
// Project 2: Some properties need alternate names for JSON serialization
public class Project2RecordList : BaseRecordList<Project2Record, Project2UserInfo>
{
[JsonProperty("allRecords")]
public override IList<Project2Record> Records { get; set; }
}
public class Project2Record : BaseRecord<Project2UserInfo> {}
public class Project2UserInfo : BaseUserInfo
{
[JsonProperty("username")]
public override string Name { get; set; }
}
I'm then happy to have 2 repositories that return Project1RecordList and Project2RecordList respectively, but at some point in my code I find myself needing to be able to handle both of these in one place. I figure that at this point I need to be able to treat both of these types as
BaseRecordList<BaseRecord<BaseUserInfo>, BaseUserInfo>
as this is the minimum required to meet the generic constraints, but trying to cast or use "as" throws up errors about not being able to convert.
Is there any way to do this, or even a more sane way to handle this situation without massive amounts of code duplication? If it makes any difference this is for a web app and there are already a large number of data classes, many of which use these nested generics.
What you are talking about is called covariance and MSDN has a great article on this here: https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx
First, create a new interface:
interface IBaseRecord<out TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecord inherit from the new interface:
public class BaseRecord<TUserInfo> : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
If done right, this should compile:
IBaseRecord<BaseUserInfo> project1 = new Project1Record();
IBaseRecord<BaseUserInfo> project2 = new Project2Record();
To expand this to the BaseRecordList, create IBaseRecordList:
interface IBaseRecordList<out TRecord, out TUserInfo>
where TRecord : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecordList inherit from that:
public class BaseRecordList<TRecord, TUserInfo> : IBaseRecordList<TRecord, TUserInfo>
And then use as such:
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project1 = new Project1RecordList();
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project2 = new Project2RecordList();
Once you have that setup, just add whatever properties or functions you need to use generically to the interfaces.

Extending classes with additional properties

I have an class object from an external library that I want to add some additional properties to.
Let's say the external class is:
public class ExternalClass
{
public string EXproperty1 {get;set;}
public string EXproperty2 {get;set;}
public string EXproperty3 {get;set;}
public ExternalClass(){}
}
and I have a list of these object which gets populated as
List<ExternalClass> listOfExternalClass=new List<ExternalClass>();
listOfExternalClass=GetListOfExternalClass();
I can extend this class by creating a new class, adding the additional properties and making the external class a property.
public class NewClass
{
public ExternalClass ExternalClass {get;set;}
public string NewProperty1 {get;set;}
public string NewProperty2 {get;set;}
public NewClass(){}
public NewClass(ExternalClass externalClass){
this.ExternalClass=externalClass;
}
}
But to convert by original list of the external classes to a list of the new classes I would have to create a new list of new classes and iterate through the original list creating a new object and adding it to the list, like
List<NewClass> listOfNewClass=new List<NewClass>();
foreach(var externalClass in listOfExternalClass)
{
listOfNewClass.Add(new NewClass(externalClass));
}
I would then be able to access the external properties like
listOfNewClass.FirstOrDefault().ExternalClass.EXproperty1;
Can I do this with inheritance or is there a more efficient method?
Ideally I would like to end up with by calling the properties like:
listOfNewClass.FirstOrDefault().EXproperty1;
This can certainly be done with inheritance. Consider the following.
//Inherit from our external class
public class NewClass: ExternalClass
{
//Note we do not have a copy of an ExternalClass object here.
//This class itself will now have all of its instance members.
public string NewProperty1 {get;set;}
public string NewProperty2 {get;set;}
//If it has parameters include those parameters in NewClass() and add them to base().
//This is important so we don't have to write all the properties ourself.
//In some cases it's even impossible to write to those properties making this approach mandatory.
public NewClass()
{
}
}
Few things to know:
Your code is called a wrapper. This is because it "wraps" another class or group of classes.
You cannot inherit from class marked as sealed.
In C# classes are not sealed by default. If they're sealed the developer has intentionally prevented you from inheriting from the class. This is usually for a good reason.
If you can actually extend the External class that would be easy to accomplish:
public class NewClass: ExternalClass
{
public string NewProperty1 {get;set;}
public string NewProperty2 {get;set;}
public NewClass(){}
public NewClass(ExternalClass externalClass){
// you would have to copy all the properties
this.EXproperty1 = externalClass.EXproperty1;
}
}
Yes inheritance is what you are looking for:
public class ExternalClass
{
public string EXproperty1 { get; set; }
public string EXproperty2 { get; set; }
public string EXproperty3 { get; set; }
public ExternalClass() { }
}
public class NewClass:ExternalClass
{
public string NewProperty1 { get; set; }
public string NewProperty2 { get; set; }
public NewClass() { }
}
If you wish for (or need) delegation instead of a copy you can do:
public class NewClass
{
public ExternalClass ExternalClass {get;set;}
public string NewProperty1 {get;set;}
public string NewProperty2 {get;set;}
public string EXproperty1 {get { return this.ExternalClass.EXproperty1; };set{ this.ExternalClass.EXproperty1 = value; }; }
public string EXproperty2 {get { return this.ExternalClass.EXproperty2; };set{ this.ExternalClass.EXproperty2 = value; }; }
public string EXproperty3 {get { return this.ExternalClass.EXproperty3; };set{ this.ExternalClass.EXproperty3 = value; }; }
public NewClass(){}
public NewClass(ExternalClass externalClass){
this.ExternalClass=externalClass;
}
}
Instead of working against specific types, work against interfaces.
Below I am showing a mix of facade pattern and adapter pattern to 'transform' external data to a well-defined interface (IDocument), effectively abstracting things your are working on.
Example 1 : query about an interface
Here are the types you'll work against:
public interface IDocument {
string Name { get; set; }
}
public interface IMetadata {
string[] Tags { get; set; }
}
This is your own representation, should you need any:
public class RichDocument : IDocument, IMetadata {
public string Name { get; set; }
public string[] Tags { get; set; }
}
This is the wrapper against external data:
(a bastard mix of facade and/or adapter concepts)
public class ExternalClass {
public string Whatever { get; set; }
}
public class ExternalDocument : IDocument /* only a basic object */ {
private readonly ExternalClass _class;
public ExternalDocument(ExternalClass #class) {
_class = #class;
}
public string Name {
get { return _class.Whatever; }
set { _class.Whatever = value; }
}
}
And a demo on how to use all that:
internal class Demo1 {
public Demo1() {
var documents = new List<IDocument> {
new ExternalDocument(new ExternalClass()),
new RichDocument()
};
foreach (var document in documents){
var name = document.Name;
Console.WriteLine(name);
// see if it implements some interface and do something with it
var metadata = document as IMetadata;
if (metadata != null) {
Console.WriteLine(metadata.Tags);
}
}
}
}
Example 2 : query about a component
This is a bit more involved by pushing the concept to treat everything in an uniform manner, you can find it in .NET framework, game development or whatever ...
Definitions you'll work against:
public interface IContainer {
IList<IComponent> Components { get; }
}
public interface IComponent {
// it can be/do anything
}
Some components you'll query about:
public interface IDocument : IComponent {
string Name { get; set; }
}
public interface IMetadata : IComponent {
string[] Tags { get; set; }
}
Your 'internal' type:
public class Container : IContainer {
public Container() {
Components = new List<IComponent>();
}
public IList<IComponent> Components { get; }
}
Your 'wrapper' against external data:
public class ExternalClass {
public string Whatever { get; set; }
}
public class ExternalContainer : IContainer {
private readonly List<IComponent> _components;
public ExternalContainer(ExternalClass #class) {
_components = new List<IComponent> {new ExternalDocument(#class)};
}
public IList<IComponent> Components {
get { return _components; }
}
}
public class ExternalDocument : IDocument {
private readonly ExternalClass _class;
public ExternalDocument(ExternalClass #class) {
_class = #class;
}
public string Name {
get { return _class.Whatever; }
set { _class.Whatever = value; }
}
}
And a usage example:
public class Demo2 {
public Demo2() {
var containers = new List<IContainer> {
new ExternalContainer(new ExternalClass()),
new Container()
};
foreach (var container in containers) {
// query container for some components
var components = container.Components;
var document = components.OfType<IDocument>().FirstOrDefault();
if (document != null) {
Console.WriteLine(document.Name);
}
var metadata = components.OfType<IMetadata>().FirstOrDefault();
if (metadata != null) {
Console.WriteLine(metadata.Tags);
}
}
}
}
Notes
The problem with inheritance is that it is a very rigid approach and generally once you start doing it and at some point you hit a wall and want to revert, it's hard to get out of it.
By working against abstractions things are more flexible and things are decoupled.
Here are two examples that might incite you to change your approach:
Composition over inheritance
Using Components

Struggling with the C# generics, is this possible?

I'm currently on design stage in writing C# .NET Core application. I'm gonna use the generics to inherit some properties among all derived classes. The goal is to archive many 2 many relation of entities able to be tagged. The app concept is funky, because tag will contain related logic as constraint entity. I have problems with the proper where statements in generic class, to be able to use inherited Tags property for all Taggable Entities.
Here is abstraction:
public interface ITaggable
{
long TagId { get; set; }
Tag Tag { get; set; }
}
public interface IEntityTag<T> : ITaggable where T : Entity
{
long EntityId { get; set; }
T Entity { get; set; }
}
public abstract class TaggableEntity<T> : Entity where T : ITaggable
{
public ICollection<T> EntityTags { get; set; }
public List<Tag> Tags { get { return EntityTags.Select(x => x.Tag).ToList(); } }
}
public abstract class ConstraintBase<TSubject, TOwner>
: ConstraintEntity where TOwner : TaggableEntity<IEntityTag<TOwner>>
{
protected ConstraintBase(ConstraintId id, string description)
{
Id = id.Value();
Name = id.ToString();
Description = description;
IsExecutable = false;
}
public IEnumerable<TSubject> Validate(IEnumerable<TSubject> items, TOwner owner)
{
return items.Where(x => Validate(x, owner));
}
public void Execute()
{
if (IsExecutable) { OnExecuting(); }
}
protected abstract bool Validate(TSubject item, TOwner owner);
public abstract void OnExecuting();
}
And here concrete classes.
public class ConstraintEntity : Entity
{
public string Name { get; set; }
public string Description { get; set; }
public bool IsExecutable { get; set; }
public ConstraintId ConstraintId => (ConstraintId)Id;
}
public class EndWorkConstraint : ConstraintBase<Activity, User>
{
public EndWorkConstraint() : base(ConstraintId.EndWorkConstraint, "Check if user is allowed to end work")
{
}
protected override bool Validate(Activity item, User owner)
{
return item.ActivityId != ActivityId.EndWork;
}
public override void OnExecuting()
{
throw new System.NotImplementedException();
}
}
public class User : TaggableEntity<UserTag>
{
public string Login { get; set; }
public string Password { get; set; }
}
The question is: am I able to modify ConstraintBase where statement, to make EndWorkConstraint class do not raising an error, and still have the tags explicit avalible?
This is my first post on the forum, and I m really forced to use Yours wisdom. I'd be glad for any tips. Thanks in advance.

Categories