0
votes

I am trying to use an interface based approach in an MVC application. However, I cannot pass my model (received from a form submission to an MVC action) to a method, instead it throws up an error saying

Cannot convert from Foo<Bar> to IFoo<IBar>


Edit: For those who rudely removed my complete question before I had a chance to update it based on comments here is an exact duplicate of the codeproject question that I had posted and thought it would make your lives easier to read there rather than here.

Hi All,

It's been a while since I've posted here but I'm having a bit of a mare and can't for the life of me remember what I'm doing wrong. I'm pretty sure it's fairly simple so hopefully you can help.

I have the following (approximated example):

public interface IFoo<T>
{   //...some other properties....
   List<T> Bars {get;set;}
}

public interface IBar
{   //...some properties...
}

public class Foo : IFoo<Bar>
{    //...some properties...
    List<Bar> Bars {get;set;}
}

public class Bar : IBar
{ //...some properties...
}

Then I have a helper class which looks a bit like this:

public interface IHelper 
{
   bool DoSomething(IFoo<IBar> model, string path);
}

public class Helper : IHelper
{ 
   public bool DoSomething(IFoo<IBar> model, string path)
   {
       //..Save model to path
       return true;
    }
}

So, the issue I have is when I try to pass an instance of Foo to my helper method I get the error: Argumet type 'Foo' is no assignable to parameter type 'IFoo<IBar>'

I'm not sure why I get this error because Foo inherits from IFoo and Bar inherits from IBar.

Additional Info

This is how the Foo class is initially getting populated. It's a form submission from a webpage. If I try to declare Foo to be Foo: IFoo<IBar> I get the error: Cannot create an instance of an interface. upon form submission. This is an MVC generated error. As a result Foo is defined as above using IFoo<Bar>

[HttpPost]
public ActionResult RequestSubmission(Foo model)
{
   if (ModelState.IsValid)
   {
       helper.DoSomething(model, Server.MapPath("/Assets/Template.docx"));
            return Json(new {IsSuccess=true});
   }
   return PartialView("Form", model);
}

Bit of scope

This project is a basic MVC5 project with no authentication. I have a controller that, using a poor mans DI controller, is having the helper class injected into it. My MVC models inherit from the interfaces to allow them to be passed through.

So effectively Foo and Bar are data models.

This was all working before I tried to re-write it to use Interfaces, as tightly coupled I had no problems at all.

So, my question in summary:

Why do I get this error, and how can I resolve it?

If you need more information or clarification on anything just yell :)

Hope you can help.


In addition to this the following comment was made in response to the above question which changed the error:

You might need

Foo : IFoo<T> where T : IBar

Then use

Foo<Bar>

rather than just Foo

This resulted in the new error:

Cannot convert from Foo<Bar> to IFoo<IBar>


Now, in response to the comment about Covariance and contravariance , I had kind of come across it in my research of the problem but I didn't understand why it was relevant. My knowledge of "Generics" is limited and I thought that what I was doing was related to Interface and DI related elements.

I'm pretty sure some one will think it appropriate to edit this again but it now has everything someone who comes along to read it needs to know about what the issue is and what the solution was (as there is a marked answer).

For reference: This is where I first posted the question Original Question on Code Project

1
codeproject does not load for me. its better to put all your question here. code project is code project. stackoverflow is stackoverflow. dont share link - M.kazem Akhgary
@Servy Best edit ever! :) - DavidG
What you would need is contravariance. Unfortunately, you can't use it because of your List<T>. Basically, because of IFoo<Bar>, your list is created as a List<Bar>. However, you try to cast your object to IFoo<IBar>, and therefore your collection to List<IBar>. Then it means that somebody would be able to add to your collection an object that isn't a Bar, resulting in a compilation error. You need to either remove the List (and use an immutable type instead), or give up on contravariance - Kevin Gosse
Note: since the information has been remove by the edit, my comment is based on this: codeproject.com/Questions/1043451/… - Kevin Gosse

1 Answers

1
votes

You can also do this with generic type conditions, for example, if you change your IHelper to this:

public interface IHelper
{
    bool DoSomething<T>(IFoo<T> model, string path) where T : IBar;
}

You can then implement it:

public class Helper : IHelper
{
    public bool DoSomething<T>(IFoo<T> model, string path) where T : IBar
    {
        //..Save model to path
        return true;
    }
}

And calling

someHelperInstance.DoSomething(someInstanceOfFoo, somePath);

No longer throws that error. This is because you are constraining the generic IFoo<T> to be any IBar derived type. You may want to read up on covariance vs contravariance on MSDN.