I am trying to create an object type (MyObject) with a linq expression of type T. My class states that the value of T must be of type BaseModel (which is an object created by me). Below is how MyObject is constructed:
public class MyObject<T> where T : BaseModel
{
public Type MyType;
public Expression<Func<T, bool>> MyExpression;
}
All of my models inherit from BaseModel. Example:
public class MyModel : BaseModel
{
public string Name { get; set; }
}
My object is used inside of a generic static class:
public static class MyStaticClass
{
private static Dictionary<MyObject<BaseModel>, string> MyDictionary = new Dictionary<MyObject<BaseModel>, string>();
public static AddMyObjectsToDictionary(List<MyObject<BaseModel>> myObjects)
{
//CODE
}
//REST OF CODE
}
Then when my app loads it does the following (Error is thrown here):
List<MyObject<BaseModel>> myObjects = new List<MyObject<BaseModel>>();
myObjects.Add(new MyObject<MyModel>()
{
MyType = typeof(MyModel),
MyExpression = p => p.Name == "myname"
});
MyStaticClass.AddMyObjectsToDictionary(myObjects);
Exact error message thrown with the namespaces to show in which project each object is located:
cannot convert from
'ProjectANamespace.MyObject<ProjectBNamespace.MyModel>'
to'ProjectANamespace.MyObject<ProjectANamespace.BaseModel>'
I need to be able to create a generic expression within MyModel however I cannot specify MyModel inside of MyStaticClass since it is meant to be a generic class which is located in another project along with BaseModel.
Anyone have any ideas how resolve this issue?
MyObject<MyModel>
isn't a MyObject<BaseModel>
, just like List<string>
isn't a List<object>
. It works the same outside of generics too - the general concept is called variance, and describes valid substitution of types. Since MyObject
is neither co-variant nor contra-variant with respect to T
, MyObject<T>
can never be a MyObject<U>
, for any T and U that aren't the same.
Generic type arguments in interfaces and delegates can be both co-variant and contra-variant, but not both at the same time. For example, your Func<T, bool>
is contra-variant, because you can substitute a type derived from T
instead of T
itself. So a Func<BaseModel, bool>
can be converted to Func<MyModel, bool>
(just as MyModel
can be passed as an argument "instead" of BaseModel
), but not vice versa. Analogously, Func<T, U>
is contra-variant with respect to T
and co-variant with respect to U
- you can't return object
from a function that has a return type of string
.
So even if you changed your definition to use an interface to add variance, you'll only be able to get contra-variance, not the co-variance you want. Too bad - there's no safe way to do that.
Instead, if you need a non-generic interface, just add a non-generic interface. That's it. Instead of List<MyObject<BaseModel>>
, you'll have List<IMyObject>
, which you can use as required (probably through casting, or just exposing the simple Expression
instead of Expression<Func<T, bool>>
).