Strange Behavior with Generics:
-
Thursday, May 01, 2008 4:58 AM
When I define a Generic at class level with a constraint, I get a compile exception , however , when I implement Generic at method level with the same constraint , all goes well ...
Wondering , wether its a bug in generics or the bug lies in my way of interpretation :-(Have Tried to explain the problem below :
Case 1:
I have defined a abstract generic class and defined the generic type at class level , with a constraint . Below is the class definition.public abstract class BaseVOMapper<T> where T:BaseVO
{
public abstract void Insert(BaseVO child , BaseVO parent );public abstract void SaveChildrens(List<T> childListToPersist, BaseVO parentVO);
}Below is the implementation o f the derived class , where FlightList is a List<FlightVO> & FlightVO: BaseVO & as per polymorphism ,I assume , I can pass List<FlightVO> where List<BaseVO> is expected.However , I endup getting the below error.
Argument '1': cannot convert from 'System.Collections.Generic.List<ValueObjects.FlightVO>' to 'System.Collections.Generic.List<ValueObjects.BaseVO>'i.e
public class FlightVO : BaseVO
{
public int FlightId;
public string FlightName;
}
public class ScheduleVOMapper: BaseVOMapper<BaseVO>
{
public override void Insert(ValueObjects.BaseVO child, ValueObjects.BaseVO parent)
{
//((ScheduleVO)child).FlightList
this.SaveChildrens(((ScheduleVO)child).FlightList, parent);
}public override void SaveChildrens(List<BaseVO> childListToPersist, BaseVO parentVO)
{
foreach (BaseVO child in childListToPersist)
{
ORlayerEngine.Persist(child, parentVO);
}
}
}------------------------------------------------------------------Code using Generic at Method Level ------------------------------------------------------------------------------------------
Case 2
// generic type specified at method level , with the same constraint
public abstract class BaseVOMapper
{
public abstract void Insert(BaseVO child , BaseVO parent );public abstract void SaveChildrens<T>(List<T> childListToPersist, BaseVO parentVO) where T:BaseVO ;
}Below is the implementation of the derived class :
public class ScheduleVOMapper: BaseVOMapper
{
public override void Insert(ValueObjects.BaseVO child, ValueObjects.BaseVO parent)
{
//((ScheduleVO)child).FlightList
this.SaveChildrens(((ScheduleVO)child).FlightList, parent);
}public override void SaveChildrens<T>(List<T> childListToPersist, BaseVO parentVO)
{
foreach (BaseVO child in childListToPersist)
{
ORlayerEngine.Persist(child, parentVO);
}
}
}Comparing Case 1 & Case 2 , in case 1 , in the derived class ScheduleVOMapper , the SaveChildrens method has the generic type explicity specified as BaseVO ,
whereas in Case 2 , the method gets the Generic Type at runtime , which anyways as per the constraint has to be of Type BaseVO.Though , I have my solution implemneted using Case 2 , would really like to get my doubt's cleared
Many Thanks
Girija
All Replies
-
Thursday, May 01, 2008 10:38 AMModerator
List<Base> and List<Derived> are distinct types, you can't convert List<Derived> to List<Base>. If it were possible, you could pass a List<Derived> to a method accepting List<Base> and that method could add an object of type Base to the list. Which violates the promise that the original list only contains objects of type Derived. -
Thursday, May 01, 2008 6:29 PMThanks Sir ,for the simplistic explanation.
-
Tuesday, August 12, 2008 12:10 AM
Great info and great response. I have a similar situation that ties into this topic. Hopefully you can help set me in the right direction.
My original intent was to create a list of a particular type and create additional Add methods that convert to that type. That seems to work fine and seems to follow the rules of a list.
public class MyObjectList : System.Collections.Generic.List<MyObject>
{/// <summary> /// Creates a MyObject item from a MyOtherObject item and /// adds it to the list /// </summary> /// <param name="item">item is converted to MyObject then added to the list</param> public void Add(MyOtherObject item) { MyObject MyObjectItem = new MyObject(item); //Create a null item in the list (just like base.Add does) if (null == MyObjectItem) { //ALERT - Does this create a null item is it necessary??? base.Add(null); } else { base.Add(MyObjectItem); } } }
Now, I want to hook up an "OnItemAdded" event to the class that fires when an item is added. So I added the event and hooked it up but it only fires on the non-standard Add. I then tried to "override" Add (which doesn't work) so I tried to "hide" the base Add with a new like below but it doesn't fire under certain circumstances...
/// <summary> /// Adds the OnLineItemAdded event to the base Add method /// </summary> /// "new" hides the base version of Add. new public void Add(DPILineItemInDetail item) { //Create a null item in the list (just like base.Add does) if (null == item) { //ALERT - Does this create a null item??? base.Add(null); } else { base.Add(item); } //Fire the OnLineItemAdded Event once a line itme has been added DPILineItemListEventArgs e = new DPILineItemListEventArgs(this.IndexOf(item)); OnLineItemAdded(e); }
The circumstance I've found that it doesn't fire under is when I add an item through the BindingSource / BindingNavigator I have it hooked to. -
Tuesday, August 12, 2008 1:01 AMModeratorThat's the problem with the "new" keyword. Consider using the .NET 3.0 System.Collections.ObjectModel.ObservableCollection<T>, it fires events when the collection changes.
Hans Passant. -
Thursday, May 26, 2011 5:51 AM
I'm pleased, as the answer to the query that I had raised 2-3 years back has finally been provided in the form of Covariance and contravariance in .Net 4.0 :).
Have blogged about it at
http://www.codeproject.com/Articles/253417/Understanding-Covariance-and-Contravariance-in-Net
Thanks Girija
- Edited by Girija Acharya Friday, October 07, 2011 6:35 AM

