4

In C#, I am trying to store a private generic variable in a non-generic class that is passed in through a generic method. The generic method is supposed to take child classes that are upcast to the base class type.

Here is an example of the issue:

class BaseModel { }
class DerivedModel : BaseModel { }

class Data<T> where T : BaseModel { }

// Class is NOT generic
class DataConsumer
{
    // How do I set this to generic?
    private Data<BaseModel> data;

    public void Set<T>(Data<T> data) where T : BaseModel
    {
        // Compile time error: 
        // Cannot convert source type 'Generic.Data<T> 
        // to target type 'Generic.Data<Generic.BaseModel>'
        this.data = data;
    }
}
bryant988
  • 43
  • 4
  • We specifically do not want to make `DataConsumer` generic since it will have many different generic methods – bryant988 Dec 02 '15 at 19:46

2 Answers2

4

That's because you're trying to assign a more derived type Data<T> to a base type Data<BaseModel>, which is not allowed in generic classes.

You have two options:

1- Make DataConsumer generic:

class DataConsumer<T> where T : BaseModel
{
    private Data<T> data;

    public void Set(Data<T> data)
    {
        this.data = data;
    }
}

2- OR, make Data an interface instead of a class and mark T as a covariant type using the out keyword:

interface IData<out T> where T : BaseModel { }

class DataConsumer
{
    private IData<BaseModel> data;

    public void Set<T>(IData<T> data) where T : BaseModel
    {
        this.data = data;
    }
}

Read more about Covariance and Contravariance in Generics here

Arian Motamedi
  • 7,123
  • 10
  • 42
  • 82
0

this.data is of type Data<BaseModel> while the function parameter data is of type Data<T : BaseModel>.

Your example kinda defeats the entire purpose of generics in the first place, but you can declare the Set() method a non-generic method, and force the client to do the cast.

private Data<BaseModel> data;

public void Set(Data<BaseModel> data)
{
  this.data = data;
}

As a side note, if you declared all entities involved to be generic, the constraint would only allow you to manipulate the objects through BaseModel's contract, so I can't see how the explicit cast would be necessary.

paolobueno
  • 1,678
  • 10
  • 9