简体   繁体   中英

implicit reference conversion error with generics inheritance

I have the following set of classes in my application:

This represents an entity, which is a SQL table. Every entity classes inherit from this interface.

public interface IBaseEntity
{
     public int Id { get; set; }
}

Then I have a class that will hold all business logic for that entity. BaseBLL holds methods that are common to all Business objects.

public class BLLPai<D>
        where D : class
{
        protected D EntidadeData;
}
public class BaseBLL<E, D> : BLLPai<D>
        where E: IBaseEntity
        where D: BaseData<E>
{
     //commom methods
}

To create a business object for the entity, it is also needed to create a Data object, which will hold all database related commands.

 public class BaseData<E> : DataPai
        where E : IBaseEntity
{
     // commom methods
}

public class DataPai
{
    protected DbConnection dbCon { get; set; }
     public DataPai()
    {
        dbCon = new DbConnection();
    }
}

This would be an example of a working entity (database table) in this design:

public class MyTable : IBaseEntity
{
    public int Id {get;set;}
    public string Name {get;set;}
}
public MyTableData: BaseData<MyTable>
{
    public List<MyTable> GetAllItems()
    {
        // return result of SELECT
    }
}
public MyTableBLL : BaseBLL<MyTable, MyTableData>
{
    public List<MyTable> GetAllItems()
    {
         // business logic
         return this.EntidadeData.GetAllItems();
    }
}

In order to use this in my architecture, I designed a Controller base class, which I am using in all of my Controllers. (I'm using this in a WebApi.Net Core project) The class is as follows:

public class MyTableController : GerenciadorLogadaCadastroController<MyTable, MyTableBLL>
{
}

[ApiController]
public abstract class GerenciadorLogadaCadastroController<Entidade, BLL> : ControllerBase
        where Entidade : IBaseEntity
        where BLL : BaseBLL<Entidade, BaseData<Entidade>>
{
}

What I am not understanding is why it gives me an error when I try to create the MyTableController class:

Error:

The type 'MyTableBLL' cannot be used as type parameter 'BLL' in the generic type or method ' GerenciadorLogadaCadastroController<Entidade,BLL> '. There is no implicit reference conversion from ' MyTableBLL ' to ' BaseBLL<MyTable, BaseData<MyTable>> '.

However, if I change the GerenciadorLogadaCadastroController and the MyTableController to this, it seems to work fine.

public abstract class GerenciadorLogadaCadastroController<Entidade, BLL, Data> : ControllerBase
        where Entidade : IBaseEntity
        where BLL : BaseBLL<Entidade, Data>
        where Data: BaseData<Entidade>
{
}

public class MyTableController : GerenciadorLogadaCadastroController<MyTable, MyTableBLL, MyTableData>
{
}

But I don't want to type the Data class because it is already included in the BLL class. I found a similar design of what I am trying to do in a Vb.Net application and it works just fine. What am I missing here that is causing that implicit reference conversion error?

I did a bit of simplification to your code to get it down to an example that shows what has gone wrong. There are a few steps.

Here is my initial simplification:

public interface IBaseEntity { }
public class MyTable : IBaseEntity { }
public class BaseData<E> where E : IBaseEntity { }
public class MyTableData : BaseData<MyTable> { }
public class BaseBLL<E, D> where E : IBaseEntity where D : BaseData<E> { }
public class MyTableBLL : BaseBLL<MyTable, MyTableData> { }
public abstract class Controller<E, B> where E : IBaseEntity where B : BaseBLL<E, BaseData<E>> { }
public class MyTableController : Controller<MyTable, MyTableBLL> { }

Now, at this point, the whole IBaseEntity portion of the code is not needed to replicate your issue so I removed it:

public class BaseData { }
public class MyTableData : BaseData { }
public class BaseBLL<D> where D : BaseData { }
public class MyTableBLL : BaseBLL<MyTableData> { }
public abstract class Controller<B> where B : BaseBLL<BaseData> { }
public class MyTableController : Controller<MyTableBLL> { }

Now I did some crafty renaming of the types:

public class Fruit { }
public class Apple : Fruit { }
public class BowlOf<D> where D : Fruit { }
public class BowlOfApples : BowlOf<Apple> { }
public abstract class Controller<B> where B : BowlOf<Fruit> { }
public class BowlOfApplesController : Controller<BowlOfApples> { }

What this is then showing is that when you define Controller<BowlOfApples> you are saying that BowlOfApples must inherit from BowlOf<Fruit> . But BowlOfApples inherits from BowlOf<Apple> and not BowlOf<Fruit> !

This constraint should make sense. Let's assume that BowlOf<T>: List<T> and we have Banana: Fruit then imagine that this code were legal:

BowlOf<Apple> apples = new BowlOf<Apple>();
BowlOf<Fruit> fruit = apples; // BOOM!!! imagine if this could be done!
Banana banana = new Banana();
fruit.Add(banana);

We have code which could add a Banana to a BowlOf<Apple> . That can't happen. The illegal thing is to cast an BowlOf<Apple> to a BowlOf<Fruit> .

Simply put BowlOf<Apple> DOES NOT INHERIT FROM BowlOf<Fruit> . This is in spite of Apple inheriting from Fruit .

Your code is making the same mistake when you try to cast BaseBLL<MyTable, MyTableData> to BaseBLL<MyTable, BaseData<MyTable>> .

You did correctly identify, in your question, that this change would work:

public abstract class Controller<F, B> where F : Apple where B : BowlOf<F> { }
public class BowlOfApplesController : Controller<Apple, BowlOfApples> { }

So when you say "But I don't want to type the Data class because it is already included in the BLL class." Well, you can't avoid that.

Here's a suggestion. Add an IBaseDLL interface.

  public interface IBaseBLL
  {
    //commom methods
  }

Then add it to the list interfaces on your BaseBLL class.

  public class BaseBLL<E, D> : BLLPai<D>, IBaseBLL
          where E : class, IBaseEntity
          where D : BaseData<E>
  {
    //commom methods
  }

Now you'll be able to change your base controller to the following:

[ApiController]
public abstract class GerenciadorLogadaCadastroController<Entidade, BLL> : ControllerBase
        where Entidade : IBaseEntity
        where BLL : IBaseBLL
{
}

-Isaac

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