tags:

views:

80

answers:

3

How to correct this design. Error because C# doesn't allow Type Covariance. How can I improve or correct this design

 public interface ITimeEvent
    {
    }

    public interface IJobTimeEvent : ITimeEvent
    {
    }

    public interface IActivityTimeEvent : ITimeEvent
    {
    }

    public interface IAssignmentTimeEvent<T> where T : ITimeEvent
    {
        T ParentTimeEvent
        {
            get;
            set;
        }
    }

    public class ScheduleJobTimeEvent : IAssignmentTimeEvent<IJobTimeEvent>
    {
        public IJobTimeEvent ParentTimeEvent
        {
           get;
           set;
        }
    }

    public class ScheduleActivityTimeEvent : IAssignmentTimeEvent<IActivityTimeEvent>
    {
        public IActivityTimeEvent ParentTimeEvent
        {
            get;
            set;
        }
    }

List<IAssignmentTimeEvent<ITimeEvent>> lst = new List<IAssignmentTimeEvent<ITimeEvent>>();
        lst.Add(new ScheduleJobTimeEvent());  //Error because C# doesn't allow Type Covariance
        lst.Add(new ScheduleActivityTimeEvent()); //Error because C# doesn't allow Type Covariance
+4  A: 

C#4.0 and .net4.0 do allow either covariance or contravariance on generic parameters (on interfaces). See: http://msdn.microsoft.com/en-us/library/dd799517.aspx

Femaref
this doesn't even work with C# 4.0
Deepak
A: 

Could you use your common interface?

// usage...
var lst = new List<IAssignmentTimeEvent>();

// extended details...
var event2 = new ScheduleActivityTimeEvent();
var byInterface = (IAssignmentTimeEvent)event2;
byInterface.ParentTimeEvent = new ActivityTimeEvent(); //this works
byInterface.ParentTimeEvent = new JobTimeEvent(); //this throws

// new interface
public interface IAssignmentTimeEvent
{
    ITimeEvent ParentTimeEvent { get; set; }
}
public interface IAssignmentTimeEvent<T> : IAssignmentTimeEvent
    where T : ITimeEvent
{
    new T ParentTimeEvent { get; set; }
}
public class ScheduleJobTimeEvent : 
    IAssignmentTimeEvent<IJobTimeEvent>
{
    public IJobTimeEvent ParentTimeEvent { get; set; }
    ITimeEvent IAssignmentTimeEvent.ParentTimeEvent
    {
        get { return ParentTimeEvent; }
        set
        {
            if (!(value is IJobTimeEvent))
                throw new InvalidCastException();
            ParentTimeEvent = value as IJobTimeEvent;
        }
    }
}

public class ScheduleActivityTimeEvent : 
    IAssignmentTimeEvent<IActivityTimeEvent>
{
    public IActivityTimeEvent ParentTimeEvent { get; set; }
    ITimeEvent IAssignmentTimeEvent.ParentTimeEvent
    {
        get { return ParentTimeEvent; }
        set
        {
            if (!(value is IActivityTimeEvent))
                throw new InvalidCastException();
            ParentTimeEvent = value as IActivityTimeEvent;
        }
    }
}
Matthew Whited
I can but its not that clean
Deepak
I don't disagree. But if you don't have .Net 4.0 you don't really have an other choice.
Matthew Whited
this doesn't even work with C# 4.0
Deepak
I just tried...
Deepak
+3  A: 

You can make this work in C# 4.0, although you need to add the covariance specifier to the interface type parameter.

However, for this to work at all, you must guarantee that the type parameter will only be used in method call results (and not parameters), which means surrendering your interface property setter. Whether or not this is acceptable for the overall design is your call.

public interface IAssignmentTimeEvent<out T> where T : ITimeEvent
{
    T ParentTimeEvent
    {
        get;
    }
}
Alan