Covariance in C# generic class

2019-02-25 02:40发布

问题:

C# 4.0 .NET 4.5 Silverlight 5 It seems weird that I cant find the solution so need some help please.

I have base class Base and derived class Child : Base. I have also helper class which has generic type to do specific work one EF entities Helper where T : EntityObject.

Child does specific work with a specific entity MyEntity : EntityObject.

So I tried:

public class Base
{
    protected Helper<EntityObject> helper;
}
public class Child : Base
{
    public Child()
    {
        helper = new Helper<MyEntity>();
    }
}

I would expect that more derived class must know about more specific generic parameter and I think that's what covariance for... But that doesn't work...

What's the 'correct' way to design class like that?

EDIT: sorry I didn't make it 100% clear why cant I achieve what I need.

a. Solution with generic Base doesn't work because user of the Base doesn't know the T type. Imagine:

public class User
{
    private Base<T> base; // this will not compile.
    public User(TypeEnum t)
    {
        if(t == TypeEnum.MyEntity) base = new Child();
...

b. Solution with Interface doesn't work because helper uses T everywhere (it is its purpose right?). Imagine it has method

public IEnumerable<T> Process(IEnumerable<T> items) { return items; }

How do I bring it up in the interface which doesn't know anything about T

回答1:

If Foo : Bar, that doesn't mean that Some<Foo> : Some<Bar>. There are two ways of doing what you want. The first is to make the base-type generic such that:

Base<T> where T : EntityObject {
    protected Helper<T> helper;
}
Child : Base<MyEntity> {...}

The second is to use a non-generic interface at the base-type, i.e. have

Base {
    protected IHelper helper;
}
Child : Base {...}

where in the latter case, Helper<T> : IHelper, for some non-generic IHelper to-be-defined.

As a side-note, you might find it easier to pass the value down in the constructor rather than using a protected field.



回答2:

I think this is what you're after:

public class Base<T> where T : EntityObject
{
    protected Helper<T> helper;
}
public class Child : Base<MyEntity>
{
    public Child()
    {
        helper = new Helper<MyEntity>();
    }
}

Edit (in response to your edit): You can add a Base, use like so:

public class Base
{
    // put anything here that doesn't rely on the type of T
    // if you need things here that would rely on T, use EntityObject and have 
    // your subclasses provide new implementations using the more specific type
}
public class Base<T> : Base where T : EntityObject
{
    protected Helper<T> helper;
}
public class Child : Base<MyEntity>
{
    public Child()
    {
        helper = new Helper<MyEntity>();
    }
}
public class User
{
    private Base myBase;
    public User(TypeEnum t)
    {
        if(t == TypeEnum.MyEntity) myBase = new Child();
        ...