Xenoprimate
Xenoprimate

Reputation: 7963

C# Generics, Abstract Classes, and Bounded Wildcards

I have a little problem when it comes to making Generics work in C#:

I have the following class:

public abstract class OphControl<TDataModel> : Control 
    where TDataModel : OphDataModel, new()
{
    /// <summary>
    /// The data model for this control.
    /// </summary>
    public TDataModel DataModel { get; private set; }

    public IEnumerable<OphControl<OphDataModel>> OphControls {
        get {
            return Controls.Cast<Control>().Where(control => control is OphControl<OphDataModel>).Cast<OphControl<OphDataModel>>();
        }
    }

    protected OphControl() {
        DataModel = new TDataModel();
    }
}

My problem is, that the OphControls property won't compile, because OphDataModel is abstract, and therefore I suppose it cannot be sure at compile-time that OphDataModel has a zero-args public constructor (notice the new() constraint on TDataModel).

I'm not even sure if this is the right way to go about it. What I really want is something like OphControl<? extends OphDataModel> from Java. I tried adding out to my TDataModel in the class declaration, but it tells me only delegates and interfaces can have covariant type parameters.

So how do I get around this pickle?

Upvotes: 1

Views: 285

Answers (2)

Phillip Scott Givens
Phillip Scott Givens

Reputation: 5464

Here are a few things of confusion. First, OphControl<TDataModel> does not derive from OphControl<OphDataModel> and you are correct about the out keyword (covariance/contravariance), they can only be used with delegates and interfaces. Since it is not a base class, you may not be able to cast it.

Unlike java, c# does not support generic wildcards (...<? extends ...>). The work around is to create a non-generic version and make it the base class.

public abstract class OphControl : Control { ... }

public abstract class OphControl<TDataModel> : OphControl 
    where TDataModel : OphDataModel, new() { ... }

Unfortunately, the base class cannot have the DataModel property because c# does not support covariant return types either.

At least you can have

public IEnumerable<OphControl> OphControls {
    get {
        return Controls.OfType<OphControl>();
    }
}

Upvotes: 1

D Stanley
D Stanley

Reputation: 152566

I thing you just need to change the generic parameter on OphControls:

public IEnumerable<OphControl<TDataModel>> OphControls {
    get {
        return Controls.Cast<Control>().Where(control => control is OphControl<TDataModel>).Cast<OphControl<TDataModel>>();
    }
}

This should compile, but without knowing more about your classes it's hard to say if this is right

Upvotes: 0

Related Questions