Arnaud F.
Arnaud F.

Reputation: 8462

Make generic class of a generic class

I want to make some things the most generic possible. Shortly, I've 2 documents to print :

So what I've so far is :

#region Models
public abstract class ThirdParty
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Student : ThirdParty
{ public DateTime Birthday { get; set; } }

public class Worker : ThirdParty
{ public string Company { get; set; } }
#endregion

#region ViewModels
public abstract class BaseViewModel<T> where T : ThirdParty
{
    public T Model { get; set; }
    public DateTime SelectedDate { get; set; }
    public CultureInfo SelectedCulture { get; set; }
}

public class StudentViewModel : BaseViewModel<Student>
{ public string SchoolInput { get; set; } }

public class WorkerViewModel : BaseViewModel<Worker>
{ public string JobInput { get; set; } }
#endregion

#region Document
public abstract class Document<T>
    where T : BaseDocumentViewModel
{
    public string Template { get; protected set; }
    public string FileName { get; protected set; }
    public T DocumentViewModel { get; protected set; }
}

public class StudentDocument : Document<StudentDocumentViewModel>
{
    public StudentDocument()
    {
        Template = "student.docx";
        FileName = $"{DocumentViewModel.BaseViewModel.Model.FirstName} {DocumentViewModel.BaseViewModel.Model.LastName}";
    }
}

public class WorkerDocument : Document<WorkerDocumentViewModel>
{
    public WorkerDocument()
    {
        Template = "worker.docx";
    }
}
#endregion

#region Document ViewModels
public abstract class BaseDocumentViewModel 
{
    public BaseViewModel<ThirdParty> BaseViewModel { get; set; }
    public string PrintingDate { get { return BaseViewModel.SelectedDate.ToString(BaseViewModel.SelectedCulture.DateTimeFormat.ShortDatePattern); } }
}

public class StudentDocumentViewModel : BaseDocumentViewModel
{
    public StudentViewModel StudentViewModel { get; set; }
    public int Age { get { return DateTime.Now.Year - StudentViewModel.Model.Birthday.Year; } }
}

public class WorkerDocumentViewModel : BaseDocumentViewModel
{
    public WorkerViewModel WorkerViewModel { get; set; }
    public string Position { get { return $"{WorkerViewModel.Model.Company} - ${WorkerViewModel.JobInput}"; } }
}
#endregion

Is it possible to simplify the BaseDocumentViewModel to make it more generic ? Needing to describe the BaseViewModel to one property and the specialized one one in another doesn't look that great.

Something like :

public abstract class BaseDocumentViewModel // What to put here ?
{
    public T ViewModel { get; set; }
    public string PrintingDate { get { return ViewModel.SelectedDate.ToString(ViewModel.SelectedCulture.DateTimeFormat.ShortDatePattern); } }
} 
// How to describe the subclasses ?

Any hint is welcome.

Upvotes: 0

Views: 70

Answers (1)

PeteGO
PeteGO

Reputation: 5791

You need an interface with a covariant generic parameter.

For example IThirdPartyViewModel<out T>.

Something like this:

// Entities
public abstract class ThirdParty
{
}

public class Student : ThirdParty
{
}

public class Worker : ThirdParty
{
}

// View models    
public interface IThirdPartyViewModel<out T>
{
}

public abstract class ThirdPartyViewModel<T> : IThirdPartyViewModel<T>
{
}

public class StudentVm : ThirdPartyViewModel<Student>
{
}

public class WorkerVm : ThirdPartyViewModel<Worker>
{
}

public abstract class DocumentViewModel<T> where T : IThirdPartyViewModel<ThirdParty>
{
}

public class StudentDocumentVm : DocumentViewModel<StudentVm>
{
}

public class WorkerDocumentVm : DocumentViewModel<WorkerVm>
{
}

Upvotes: 1

Related Questions