views:

175

answers:

3

I was wondering if anyone could tell me if this kind of behaviour is possible in C# 4.0

I have an object hierarchy I'd like to keep strongly typed. Something like this

class ItemBase {}

class ItemType<T> where T : ItemBase 
{
   T Base { get; set; }
}


class EquipmentBase : ItemBase {}
class EquipmentType : ItemType<EquipmentBase> {}

What I want to be able to do to have something like this

ItemType item = new EquipmentType();

And I want item.Base to return type ItemBase. Basically I want to know if it's smart enough to strongly typed generic to a base class without the strong typing. Benefit of this being I can simply cast an ItemType back to an EquipmentType and get all the strongly typedness again.

I may be thinking about this all wrong...

A: 

No, because ItemType as far as the compiler is concerned is a separate type from ItemType<EquipmentBase> or ItemType<Foo>. All three are treated as unique types and they cannot represent one another.

In your class declarations, you declared it as ItemType<T> and so ItemType would be an undefined type which would not compile.

At best, you could use an ItemType<EquipmentBase> object to represent EquipmentType or any other class derived from ItemType<EquipmentBase> but not ItemType<PersonType>.

Yadyn
That won't work either in C# 4 because of the 2 reasons I noted in my answer. But even if ItemType<T> was instead IItemType<T>, the fact that it needs T as an in and out parameter effectively rules out the desired usage.
Josh Einstein
+4  A: 

You're talking about covariance which would allow you to do:

ItemType<object> item = new EquipmentType();

You couldn't do this in C# 4 because of the following reasons:

  1. Generic covariance only works on interfaces, arrays, and delegate types, not base classes
  2. Your ItemType class uses T as an in/out type parameter meaning it receives a T and also returns a T.

Number 2 is the main issue because if it were allowed, then the following code would have to be compilable, yet fail at runtime.

// this will not work
ItemType<object> item = new EquipmentType();
item.Base = new Object(); // this seems ok but clearly isn't allowed

Covariance and Contravariance FAQ

Josh Einstein
A: 

I don't think that the new features of C# 4.0 will help you out there. However, there is a way around this which already works since generics were introduced: you create an abstract base class with the same name as the generic class and put all members which you want and which don't need to accept or return an argument of the generic type, like so:

class ItemBase {
}

abstract class ItemType {
    public ItemBase Base {
        get { return GetItemBase(); }
        set { SetItemBase(value); }
    }

    protected abstract void SetItemBase(ItemBase value);

    protected abstract ItemBase GetItemBase();
}

class ItemType<T> : ItemType where T : ItemBase {
    protected override sealed void SetItemBase(ItemBase value) {
        Base = (T) value;
    }

    protected override sealed ItemBase GetItemBase() {
        return Base;
    }

    public new T Base { get; set; }
}
Lucero