简体   繁体   中英

C# Use Generics to Call Overridden Method

I think I am a bit mixed up at this point, but I can't seem to be able to solve this issue.

I have an Interface ILinkHandler<T> and 4 other handler classes (inheriting from that interface) that validate different structures of links. From that interface, I have a Task<List<T>> Validate() function that does the validation of links and returns a Task> of results. Depending on T , I return a different model on Validate() (I have 4 different models).

My console app does the following. It calls the Task<List<T>> Validate(); method of every link type and creates some logs after getting results (note that Validate() is async ). Every log is a little bit different, since the model is different, so I overrode a method named WriteResults(ModelX results, string name) and from the ModelX type (see end of question, I posted 2 examples), I do some stuff different (not important on this scope I think, but I can provide details if necessary). This method is NOT async.

I wanted to use Generics and my Interface to create a method ( ValidateModel<T> ) that handles the right call to the overridden method WriteResults from the type of the model and calls the Validate() method from the interface.

The code below is that I did that worked, but the if part resembles what I currently have in my main and I want to avoid.

public void ValidateModel<T>(ILinkHandler<T> handler, string name) where T : class
{
    Console.WriteLine($"Validating {name}");
    var results = handler.Validate();

    if (typeof(T) == typeof(InternalLinksModel))
    {
        WriteResults(results.Result as List<InternalLinksModel>, name);
    }
    else // continue with other models
}

Here is what I have in my main:

private static void Main(string[] args) 
{
    Console.WriteLine("Validating External_Links");
    var resultsForExternalLinks = ExternalLinkHandler.Validate();
    WriteResults(resultsForExternalLinks.Result, "External_Links");

    Console.WriteLine("Validating Image_Links");
    var resultsForImageLinks = ImageLinkHandler.Validate();
    WriteResults(resultsForImageLinks.Result, "Image_Links");

// and so on
}

I want more something like this if possible, but this does not compile:

public void ValidateModel<T>(ILinkHandler<T> handler, string name) where T : class
{
    Console.WriteLine($"Validating {name}");
    var results = handler.Validate();

    WriteResults<T>(results.Result as List<T>, name);
}

Here is the definition of WriteResults (note that since it's overridden, I have 4 methods with their signature changing in the type of the list):

private void WriteResults(List<InternalLinksModel> results, string filename) { // Logs results into folder to display in jenkins }

private void WriteResults(List<PdfLinksModel> results, string filename) { // Logs results into folder to display in jenkins }

// and so on

EDIT: Adding more code

Interface:

public interface ILinkHandler<T>
{   
    Task<List<T>> Validate();
}

Example of Handler Class inheriting the interface:

public class InternalLinkHandler : ILinkHandler<InternalLinksModel>
{
    public List<InternalLinksModel> InternalLinks = new List<InternalLinksModel>();

    public async Task<List<InternalLinksModel>> Validate()
    {
        // Here set up my tests, call tasks that modifies InternalLinks List and I await for its results
        return InternalLinks 
    }

Main Class (named XmlLinkCheckerValidator ) where my code runs currently (and it works):

public class XmlLinkCheckerValidator
{
    // References to all modes
    public ExternalLinkHandler ExternalLinkHandler => new ExternalLinkHandler();
    public ImageLinkHandler ImageLinkHandler => new ImageLinkHandler();
    public InternalLinkHandler InternalLinkHandler => new InternalLinkHandler();
    public PdfLinkHandler PdfLinkHandler => new PdfLinkHandler();

    public void ValidateIPack()
    {
        InitialSetup();

        Console.WriteLine("Validating External_Links");
        var resultsForExternalLinks = ExternalLinkHandler.Validate();
        WriteResultsForIPacksInCsv(resultsForExternalLinks.Result, "External_Links");

        Console.WriteLine("Validating Image_Links");
        var resultsForImageLinks = ImageLinkHandler.Validate();
        WriteResultsForIPacksInCsv(resultsForImageLinks.Result, "Image_Links");

        Console.WriteLine("Validating Internal_Links");
        var resultsForInternalLinks = InternalLinkHandler.Validate();
        WriteResultsForIPacksInCsv(resultsForInternalLinks.Result, "Internal_Links");

        // Console.WriteLine("Validating Pdf Links");
        // var results = XmlLinkExtractorFromIPacks.PdfLinkHandler.Validate();
        // WriteResultsForIPacks(results, "Pdf Links");
    }

    private void WriteResultsForIPacksInCsv(List<InternalLinksModel> results, string filename) { logging results }

    private void WriteResultsForIPacksInCsv(List<ExternalLinksModel> results, string filename) { logging results }

    private void WriteResultsForIPacksInCsv(List<ImageLinksModel> results, string filename) { logging results }

    private void WriteResultsForIPacksInCsv(List<PdfLinksModel> results, string filename) { logging results }

    private void WriteResultsForIPacksInCsv(List<InternalLinksModel> results, string filename) { logging results }

I finally refactored all my code and it looks much clear. As I said, in the beginning, I think I got a bit mixed up and it all started from one bad design decision I took.

In short, I added a new method, WriteResults(string filename) . Doing so, I implemented the method and removed the overridden method ( WriteResultsForIPacksInCsv(List<WhateverModelIHad> results, string filename) ) from my "Main" class. From that, I changed the Validate method signature in the interface to Task<List<PossibleResults>> Validate() and since every model has that in common, I removed the generic in the interface. I can now call my handlers the following way:

public void Validate(ILinkHandler handler, string filename)
{
    Console.WriteLine($"Validating {filename}");
    var results = handler.Validate();
    SetUpResultsStatistics(results.Result, $"{filename}_Statistics");
    handler.WriteResults(filename);
}

I created a function named void SetUpResultsStatistics(List<PossibleResults> results, string filename) which gives statistics of results and it is common to all handlers (thus, to avoid duplication I put it there).

Code is now much clearer and it now does not use any generics nor overridden method. I, however, am still curious as to how should I handle a case like that and will try to formulate it in another question with a much simpler example.

Thank you all for your comments, really appreciated!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM