none
Strange Behavior with Generics:

    Question

  •  

    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

     

    Thursday, May 01, 2008 4:58 AM

Answers

  • 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 10:38 AM
    Moderator

All replies

  • 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 10:38 AM
    Moderator
  • Thanks Sir ,for the simplistic explanation.

     

    Thursday, May 01, 2008 6:29 PM
  • 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 12:10 AM
  • That'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.
    Tuesday, August 12, 2008 1:01 AM
    Moderator
  • 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

    Thursday, May 26, 2011 5:51 AM