0

I'm having a problem with an interface, and I'm not sure how to resolve it.

Here's the scenario:

// IApplicationForm does nothing other than ensure it's an
// application form.
public class MortgageApplicationForm : IApplicationForm {}

internal interface IDataAdapter
{
    StringContent FormatOutput<TForm>(TForm form) where TForm : IApplicationForm;
}

internal class DataAdapter : IDataAdapter
{
    public StringContent FormatOutput<TForm>(MortgageApplicationForm form) 
        where TForm : IApplicationForm
    {
        return new StringContent("", Encoding.UTF8, MediaType.Json.Description());
    }
}

DataAdapter is the non-generic DataAdapter for MortgageApplicationForm, so I'd like to use the concrete class rather than the IApplicationForm interface for the FormatOutput method.

However, I get a message to say that IDataAdapter doesn't implement the method with that signature.

I understand that <TForm>(TForm form) isn't the same as <TForm>(MortgageApplicationForm form) but I thought it would be acceptable because MortgageApplicationForm implements the IApplicationForm interface.

I was wrong - any advice appreciated.

Update

Scott's solution is correct but it doesn't work in this instance because of the way DataAdapter is instantiated using Reflection:

    public static IDataAdapter GetDataAdapter(string apiKey)
    {
        return (IDataAdapter)Activator.CreateInstance(
            Type.GetType($"My.Base.Namespace.{apiKey}.DataAdapter.cs"));
    }
John Ohara
  • 2,821
  • 3
  • 28
  • 54
  • 1
    MortgageApplicationForm must Implement IApplicationForm, it doesn't inherit it. – Steve Todd Jun 07 '19 at 14:43
  • @SteveTodd - poor terminology from me. IApplicationForm doesn't contain anything, so as you can see from the code snippets, it does implement it. – John Ohara Jun 07 '19 at 14:47
  • 2
    @JohnOhara syntax error `public StringContent FormatOutput(MortgageApplicationForm form)` should be `public StringContent FormatOutput(TForm form)` just like it is defined in the interface – Nkosi Jun 07 '19 at 14:48

1 Answers1

2

It looks like what you want to do is this:

internal interface IDataAdapter<TForm> where TForm : IApplicationForm
{
    StringContent FormatOutput(TForm form);
}

internal class MortgageApplicationFormAdapter : IDataAdapter<MortgageApplicationForm>
{
    public StringContent FormatOutput(MortgageApplicationForm form)
    {
        return new StringContent("", Encoding.UTF8, MediaType.Json.Description());
    }
}

That you want to specify MortgageApplicationForm as the form type indicates that every implementation of the interface would also be for a specific form type.

If you wanted one implementation that worked with any form type then the generic argument would be on the method. If you want every implementation to handle a specific form type then the generic argument would on the interface itself.

Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
  • @Nkosi, Thanks, missed that. I shouldn't really post anything without checking it in VS. Even when I think it's so simple that I can't make a mistake, I'm wrong. – Scott Hannen Jun 07 '19 at 14:54
  • 2
    Happens to the best of us. That is what the extra community eyes are for. – Nkosi Jun 07 '19 at 14:55
  • It's definitely removed the syntax error Scott., My only concern is that the DataAdapter class instance is created using Reflection - do you think it's likely to break with the addition of the interface? – John Ohara Jun 07 '19 at 15:02
  • If you're using reflection to create an instance of this class and then cast it to the interface that's fine, unless you also have to use reflection to construct the generic interface. You can do that too, but then you'll have a different problem in a different place. I don't have enough information to fully understand, but what you're describing is potentially a problem that's separate from this one. – Scott Hannen Jun 07 '19 at 15:10
  • @Scott - It does actually break other things. I've posted an update showing the method used to create the DataAdapter instance, which isn't currently generic (each instance is within it's own folder). – John Ohara Jun 07 '19 at 15:25
  • That should be a separate question. This one is about how to implement the interface, and it's easy. The other one is potentially a lot more complicated because I suspect that it reflects (no pun intended) a design issue behind the immediate problem. – Scott Hannen Jun 07 '19 at 15:28