1

Edit: I've received some comments identifying this as a duplicate and linking to definitions of the virtual, override, and other such keywords, however that isn't the complete answer that I am looking for. I state below in the original post that I've tried to use the virtual and override keywords but got a compiler error. I originally omitted those keywords from the code because it did not compile but I've put them in now to focus the problem on the fact that the derived class' method signature doesn't match up enough to be able override the base class' method. Simply adding those keywords does not solve my problem. After reading the responses I was given I realized that I need help to make the code compile and achieve the results I'm looking for.


Hi I have code that looks a lot like:

// WeeklyReport.cs
public class WeeklyReport: Report {
    // class definitions
}

// ReportService.cs
public class ReportService
{
    public async Task<ResponseMessage> ChangeStatus<T>(string reportId, string status) where T:Report, new()
    {
         var report = await SelectReportById<T>(reportId);
         // do stuff
         return await SetStatus(change, report);
    }

    public virtual async Task<ResponseMessage> SetStatus<T>(StatusChange change, T report) where T : Report, new()
    {
        // do stuff
    }
}

// WeeklyReportService.cs
public class WeeklyReportService : ReportService
{
    // This method does not compile!!!
    public override async Task<ResponseMessage> SetStatus(StatusChange change, WeeklyReport report)
    {
        // do stuff
    }
}

Short description of the above code:

I have two model classes, Report & WeeklyReport. WeeklyReport is a subclass of Report

I have two service classes, ReportService & WeeklyReportService. WeeklyReportService is a subclass of ReportService and is attempting to override its SetStatus() method

Problem:

When I call ChangeStatus() on an instance of WeeklyReportService. I want SetStatus() as defined in WeeklyReportService to be called instead of SetStatus() as defined in ReportService.

I have already tried adding the virtual and override keywords to the method signatures but I get 'WeeklyReportService.SetStatus(StatusChange change, WeeklyReport report)': no suitable method found to override.

Does anyone know how I can achieve this?

Thanks, Ethan

Ethan Melamed
  • 367
  • 1
  • 11
  • `SetStatus` in `ReportService` needs to be `virtual`, then you can `override` it in `WeeklyReportService` – JSteward Oct 23 '18 at 21:29
  • Possible duplicate of [The difference between virtual, override, new and sealed override](https://stackoverflow.com/questions/6162451/the-difference-between-virtual-override-new-and-sealed-override) – eocron Oct 23 '18 at 21:30
  • https://stackoverflow.com/questions/6162451/the-difference-between-virtual-override-new-and-sealed-override – eocron Oct 23 '18 at 21:30
  • Hi and thank you for your responses. Note that simply adding those keywords causes a compiler error. Please see my edit in the original post for my explanation of why this is not a duplicate of those posts. – Ethan Melamed Oct 24 '18 at 02:33

2 Answers2

1

There's a minor confusion of concepts going on here. There is no templating (specialization) of generics in C#. You can override with exactly the sames signature a virtual method, but you can't provide a type-specific implementation that would be auto-picked and run based on type.

Here, you'd override with parameter type, T, but your derived implemenation (which unlike base class, knows WeeklyReport exists) would check type, cast to WeeklyReport and call the type specific overload (not override).

What it sounds like is desired is an abstract ReportService where T is Report and a derived WeeklyReportService: ReportService.

1

It's not possible (AFAIK) to do exactly what you want but you should be able to do:

public class WeeklyReportService : ReportService
{
    public override Task<ResponseMessage> SetStatus<T>(StatusChange change, T report) where T : Report, new()
    {
        if (report is WeeklyReport weeklyReport)
            return SetWeeklyReportStatus(change, weeklyReport);
        return super.SetStatus(change, report);
    }
    async Task<ResponseMessage> SetWeeklyReportStatus(StatusChange change, WeeklyReport report)
    {
        report.Status = change;
        await report.SaveChanges(); // or whatever
        return MakeResponseMessage();
    }
}

(you could add 'async' and 'await' to ReportService.SetStatus, but without knowing more about how your code might be implemented I couldn't say if it's likely to be useful)

Dylan Nicholson
  • 1,301
  • 9
  • 23
  • Thanks for your answer! This exact fix would have done it for me except I got an error saying that I would have to upgrade to c# 7.1 to use that syntax. Turns out we're still on 7.0 and upgrading wasn't an option for me at this time (large code base, testers not willing to do full regression for this story). I ended up overriding the SetStatus() method exactly as you said and used a helper function as well except that I had the helper method take a dynamic rather than WeeklyReport as the type and then I just typecasted it in first line as a workaround until we upgrade. Thank again! – Ethan Melamed Oct 25 '18 at 15:17