views:

150

answers:

3

I have entity class generated by E-R designer that I have modified a little. This is declaration:

public abstract partial class Preference<T> : EntityObject, IPreference<T>

Then there is another entity class declared as follows:

public partial class BoolPref : Preference<bool>

so BoolPref inherits from Preferences<bool>. Now I have this generated property:

public ObjectSet<Preference<object>> Preferences
    {
        get
        {
            if ((_Preferences == null))
            {
                _Preferences = base.CreateObjectSet<Preference<object>>("Preferences");
            }
            return _Preferences;
        }
    }
    private ObjectSet<Preference<object>> _Preferences;

When I try to add new BoolPref to this ObjectSet as follows

context.Preferences.AddObject(new BoolPref ());

I get compile time error.

How can I add instances of BoolPref to Preferences ? Where is the mystake ?

+2  A: 

The short answer is that generics, out of the box, don't handle polymorphism in the same way which "normal" objects handle polymorphism-- Preference<object> and Preference<bool> are two mutually incompatible occasions of Preference<T> not a supertype/subtype.

The good news is that with .NET 4.0, you can use covariance to get the desired effect.

The other issue here is I'm not sure how the EF is going to be able to handle your class in terms of persistence as it probably has no idea what to do with Preference<T>.

Wyatt Barnett
So what do you suggest ? I'm not sure what you mean by "it probably has no idea what to do with Preference". It cannot persist generic classes ? Or what is the issue?
drasto
@Wyatt, note that covariance might not help in this case, due to what Eric pointed out above. Covariance works nicely with immutable interfaces, like `IEnumerable<T>`, that only return values, but it doesn't allow you to pass values in.
Dan Bryant
+12  A: 

Suppose Preference<T> has a read-write property called Value of type T. Now suppose the type system works the way you'd like it to:

var set = new ObjectSet<Preference<object>>();
set.Add(new Preference<bool>());
set.First().Value = "hello"; 

That's legal at compile time because set.First().Value has compile time type of object, not bool. But at runtime it is of type bool, and you just called a setter that takes a bool and passed a string, corrupting memory in the CLR which then crashes and dies horribly.

That's why this is not legal. In C# 4 you can have generic covariance and contravariance only if it is provably typesafe, and only if the generic type is an interface or delegate, and only if the varying type argument is of reference type.

Eric Lippert
I understand (and there really is property of type T called Value in Preference<T>).Any idea of a better design that would do the trick ?
drasto
@drasto: Well, what are you going to *do* with the collection of preferences-of-arbitrary-type? I wouldn't want to design a type hierarchy without knowing what operations were going to be performed on the instances.
Eric Lippert
+1  A: 

The way around this is to have an IPreference interface that exposes the underlying value as an Object, so you can have an ObjectSet<IPreference>. The Preference<T> explicit implementation of IPreference would then need to check at runtime whether it is being provided values that match the underlying type.

Dan Bryant
That means that I lose type-safety. The whole point in using this generics was not to lose it.
drasto
Is `ObjectSet<Preference<object>>` more type-safe? It's essentially the same concept, only expressed as an interface rather than as a concrete `Preference<object>`. The problem here is that you have conflicting design goals; you want type safety, but you want to be able to accommodate any type. At that point, you need to start doing your own type management at runtime, though the CLR will be happy to yell at you if you try to perform an invalid cast. This is a very common design problem and it's basically what all generic interfaces to databases have to deal with.
Dan Bryant
OK so if I follow your advice I change all generics classes to normal classes right ? I'm not sure I understand you... Could you provide code example ?
drasto
@drasto, Generics are quite useful in cases where you know the type at compile-time. It's not clear exactly what you're trying to accomplish with your collection, however. You can always take the `IPreference` instance and up-cast it to an `IPreference<bool>` if you have code that's type-aware (for instance, perhaps you associate a CheckBox with an IPreference and it has an internal check at runtime to verify that it's associated with an `IPreference<bool>`. It depends on what you're trying to do.
Dan Bryant