I am trying to learn how to create generic classes with c#. Can someone explain why I get a compile error when I run this program.
I have created the IZooAnimal interface. All zoo animals will implement this interface.
public interface IZooAnimal
{
string Id { get; set; }
}
public class Lion : IZooAnimal
{
string Id { get; set; }
}
public class Zebra : IZooAnimal
{
public string Id { get; set; }
}
The ZooCage will hold animals of the same Type
public class ZooCage<T> where T : IZooAnimal
{
public IList<T> Animals { get; set; }
}
The zoo class have cages
public class Zoo
{
public IList<ZooCage<IZooAnimal>> ZooCages { get; set; }
}
The program that uses the classes
class Program
{
static void Main(string[] args)
{
var lion = new Lion();
var lionCage = new ZooCage<Lion>();
lionCage.Animals = new List<Lion>();
lionCage.Animals.Add(lion);
var zebra = new Zebra();
var zebraCage = new ZooCage<Zebra>();
zebraCage.Animals = new List<Zebra>();
zebraCage.Animals.Add(zebra);
var zoo = new Zoo();
zoo.ZooCages = new List<ZooCage<IZooAnimal>>();
zoo.ZooCages.Add(lionCage);
}
}
When I compile I get the following error:
Error 2 Argument 1: cannot convert from 'ConsoleApplication2.ZooCage<ConsoleApplication2.Lion>' to 'ConsoleApplication2.ZooCage<ConsoleApplication2.IZooAnimal>'
What changes do I have to do in order to make my program run?
#DanielMann's answer is quite good, but suffers from one drawback: the original IList interface cannot be used with the ICage interface. Instead, the ICage has to expose a ReadOnlyCollection, and expose a new method called CageAnimal.
I've also re-written the code using a similar approach. My ICage implementation is much weaker, but it allows you to stick with IList semantics inside.
public interface IZooAnimal
{
string Id { get; set; }
}
public class Lion : IZooAnimal
{
public string Id { get; set; }
}
public class Zebra : IZooAnimal
{
public string Id { get; set; }
}
public interface ICage
{
IEnumerable<IZooAnimal> WeaklyTypedAnimals { get; }
}
public class Cage<T> : ICage where T : IZooAnimal
{
public IList<T> Animals { get; set; }
public IEnumerable<IZooAnimal> WeaklyTypedAnimals
{
get { return (IEnumerable<IZooAnimal>) Animals; }
}
}
public class Zoo
{
public IList<ICage> ZooCages { get; set; }
}
class Program
{
static void Main(string[] args)
{
var lion = new Lion();
var lionCage = new Cage<Lion>();
lionCage.Animals = new List<Lion>();
lionCage.Animals.Add(lion);
var zebra = new Zebra();
var zebraCage = new Cage<Zebra>();
zebraCage.Animals = new List<Zebra>();
zebraCage.Animals.Add(zebra);
var zoo = new Zoo();
zoo.ZooCages = new List<ICage>();
zoo.ZooCages.Add(lionCage);
}
}
Since you want to have multiple cages, but each type of cage can only hold one animal, your model is slightly off.
I rewrote the code as follows:
IZooAnimal is unchanged.
There's a covariant interface ICage that accepts any type of IZooAnimal. That allows you to have a strongly-typed cage for every type of animal.
Then, I have a Cage concrete implementation of ICage. Cage is generic, but you could just as easily make it an abstract class and then make animal-specific cage implementations. For example, if your zebra needs to be fed grass, and your lion needs to be fed meat, you could specialize the implementations of their cages.
Here's the complete code:
public interface IZooAnimal
{
string Id { get; set; }
}
public interface ICage<out T> where T : IZooAnimal
{
IReadOnlyCollection<T> Animals { get; }
}
public class Cage<T> : ICage<T> where T: IZooAnimal
{
private readonly List<T> animals = new List<T>();
public IReadOnlyCollection<T> Animals
{
get
{
return animals.AsReadOnly();
}
}
public void CageAnimal(T animal)
{
animals.Add(animal);
}
}
public class Lion : IZooAnimal
{
public string Id { get; set; }
}
public class Zebra : IZooAnimal
{
public string Id { get; set; }
}
public class Zoo
{
public IList<ICage<IZooAnimal>> Cages { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
var lion = new Lion();
var zebra = new Zebra();
var lionCage = new Cage<Lion>();
lionCage.CageAnimal(lion);
var zebraCage = new Cage<Zebra>();
zebraCage.CageAnimal(zebra);
var zoo = new Zoo();
zoo.Cages.Add(lionCage);
zoo.Cages.Add(zebraCage);
}
}
You should define your lists not with the concrete type that implements the interface, but with the interface:
var lionCage = new ZooCage<IZooAnimal>();
lionCage.Animals = new List<IZooAnimal>();
Then your code will work as expected.
The initial code did not work, because it is not allowed to convert concrete types to a generalised type (as #default.kramer pointed out covariance and contravariance).
The solution that i came up is following:
// your ZooCage is still generic
public class ZooCage<T>
{
// but you declare on creation which type you want to contain only!
private Type cageType = null;
public ZooCage(Type iMayContain)
{
cageType = iMayContain;
animals = new List<T>();
}
// check on add if the types are compatible
public void Add(T animal)
{
if (animal.GetType() != cageType)
{
throw new Exception("Sorry - no matching types! I may contain only " + cageType.ToString());
}
animals.Add(animal);
}
// should be generic but not visible to outher world!
private IList<T> animals { get; set; }
}
This code allows you to do:
var lion = new Lion();
var lionCage = new ZooCage<IZooAnimal>(typeof(Lion));
lionCage.Add(lion);
var zebra = new Zebra();
var zebraCage = new ZooCage<IZooAnimal>(typeof(Zebra));
zebraCage.Add(zebra);
But it will throw an error on:
zebraCage.Add(lion);
Now the zoo can be safely extended.
Related
I am stuck on interfaces and inheritance. If I implement two classes who both have an interface each, how would I be able to add the properties of Class A and B together? For instance I wanted to associate firstitem with the seconditem.
public interface IAlpha
{
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml, UriTemplate = "/AddBravoToAlpha/{firstitem}/{seconditem}")]
void AddBravoToAlpha(int firstitem, int seconditem);
}
public interface IBravo
{
// what goes in here?
}
public Class Alpha
{
public Alpha()
{
AlphaAdd = new List<Bravo>();
}
int Firstitem { get; set }
public List<Bravo> AlphaAdd { get; set; }
}
public Class Bravo
{
public Bravo()
{
BravoAdd = new List<Alpha>(); //not sure if Bravo can access Alpha (derived class)
}
int Seconditem { get; set }
Guid Indexer { get; set }
public List<Alpha> BravoAdd { get; set; }
}
public Class BravoDoesAlpha : IBravo, IAlpha //????
{
List<Alpha> alpha = new List<Alpha>();
List<Bravo> bravo = new List<Bravo>();
public void AddBravoToAlpha(int firstitem, int seconditem)
{
var result = alpha.Where(n => String.Equals(n.Firstitem, firstitem)).FirstOrDefault();
var result1 = bravo.Where(n => String.Equals(n.Seconditem, seconditem)).FirstOrDefault();
if (result != null)
{
result.BravoAdd.Add(new Alpha() { Firstitem = firstitem });
}
if (result1 != null)
{
result1.AlphaAdd.Add(new Bravo() { Seconditem = seconditem });
}
}
}
Okay, so the question you are being asked is basically one about how to do a certain kind of refactoring known as "extracting" an interface.
This is one of the more easy refactorings to do and to understand if you understand interfaces vs. types.
All interfaces are types, but not all types are interfaces.
Now let's assume we are dealing in a world with two families of types: classes and interfaces (as in your example).
Instead of working your example directly, I will work a different but clearer example that does not use Alpha, Bravo, Charlie, Epsilon, etc. because this kind of stuff makes it harder to see the meaning.
First, here's the before:
public class Dog
{
public void Bark() { Console.WriteLine("Woof!"); }
public int NumberOfDogLegs { get { return 2; } }
public int NumberOfDogFriends { get; set; } // this can be set
private string SecretsOfDog { get; set; } // this is private
}
public class DoorBell
{
public void Chime() { Console.WriteLine("Ding!"); }
}
To extract the interface of a class, simply, well, extract all the public members of the class to an interface.
public interface IDog
{
void Bark();
int NumberOfDogLegs { get; }
int NumberOfDogFriends { get; set; }
}
public interface IDoorBell
{
void Chime();
}
Now to really make use of OOP, you can find a way to abstract IDog and IDoorBell. What do they have in common? Well, the obvious one is they both make a noise. So we make a new interface, public interface IMakeANoise and say that IDog and IDoorBell both implement it.
public interface IMakeANoise
{
void MakeNoise();
}
public interface IDog : IMakeANoise
{
void Bark();
int NumberOfDogLegs { get; }
int NumberOfDogFriends { get; set; }
}
public interface IDoorBell : IMakeANoise
{
void Chime();
}
And now we have a new method to implement on Dog and DoorBell.
public class Dog : IDog
{
public void Bark() { Console.WriteLine("Woof!"); }
public int NumberOfDogLegs { get { return 2; } }
public int NumberOfDogFriends { get; set; } // this can be set
private string SecretsOfDog { get; set; } // this is private
public void IMakeANoise() { Bark(); }
}
public class DoorBell : IDoorBell
{
public void Chime() { Console.WriteLine("Ding!"); }
public void IMakeANoise() { Chime(); }
}
Now let's say we are actually writing a video game and Dog and DoorBell are both things that we can show on the screen. Well, this makes them a lot bigger because we will need to provide more information like their coordinates, their states, etc.
In this case, Dog and DoorBell may be very different to us but are similar enough to potentially merit sharing a base class. (Really, this is a stretch, but it does get the point across.)
Without adding all those new interfaces and their implementations, let's just do the "sharing a base class" refactoring for what we already have.
public class RenderableThing : IMakeANoise, IDoAThousandOtherThings
{
protected virtual string MyNoiseToMake { get { return ""; } }
public virtual void MakeANoise()
{
Console.WriteLine(MyNoiseToMake);
}
}
public class Dog : RenderableThing, IDog
{
protected override string MyNoiseToMake { get { return "Woof!"; } }
public void Bark() { MakeANoise(); } // see what we did there?
// Notice that I am not declaring the method MakeANoise because it is inherited and I am using it by overriding MyNoiseToMake
public int NumberOfDogLegs { get { return 2; } }
public int NumberOfDogFriends { get; set; } // this can be set
private string SecretsOfDog { get; set; } // this is private
}
public class DoorBell : RenderableThing, IDoorBell
{
public void Chime() { Console.WriteLine("Ding!"); }
public override void MakeANoise()
{
Chime(); Chime(); Chime(); //I'll do it my own way!
}
}
You may wonder, what's the point? So we can do this...
IMakeANoise dogNoiseMaker = new Dog();
IMakeANoise doorBellNoiseMaker = new DoorBell();
IList<IMakeANoise> listOfNoiseMakers = new List<IMakeANoise>();
listOfNoiseMakers.Add(dogNoiseMaker);
listOfNoiseMakers.Add(doorBellNoiseMaker);
foreach (IMakeANoise noiseMaker in listOfNoiseMakers)
{
noiseMaker.MakeANoise();
}
// This will output
// Woof!
// Ding!
// Ding!
// Ding!
I'm going to take a shot in the dark and venture a guess that you don't quite understand what interfaces and inheritance is. I'll start off by explaining what interfaces are:
Interfaces contain only the definitions of methods, properties, events or indexers that an inheriting class must implement.
For example:
interface IExample
{
void HelloWorld();
}
class ExampleClass : IExample
{
public void HelloWorld()
{
Console.WriteLine("Hello world.");
}
}
Now for Inheritance; when you derive a class from a base class the derived class will inherit all members of the base class except for the constructors. Note: Depending on the accessibility of the members in the base class it's children may or may not be able to access the parents members.
public class Animal
{
public string Name { get; set; }
public Animal(string name)
{
Name = name;
}
public void Talk()
{
Console.WriteLine("{0} is talking", Name);
}
}
public class Cat : Animal
{
public Cat(string name) : base(name) { }
}
public class Dog : Animal
{
public string FurColor { get; set; }
public Dog(string name, string furColor) : base(name)
{
FurColor = furColor;
}
public void Greeting()
{
Console.WriteLine("{0} has {1} fur.", Name, FurColor);
}
}
class Program
{
static void Main(string[] args)
{
var cat = new Cat("Rex");
cat.Talk();
var dog = new Dog("Beanie", "Red");
dog.Talk();
}
}
I often end up writing classes like this:
public class Animal
{
public string Colour { get; set; }
public int Weight { get; set; }
public Animal(Dog data)
{
this.Colour = data.Colour;
this.Weight = data.Weight;
}
public Animal(Cat data)
{
this.Colour = data.Colour;
this.Weight = data.Weight;
}
}
When you have lots of properties and types then you quickly end up with a lot of boiler plate code. Ideally in this situation I would just create an IAnimal interface and reference that. I'm currently in a situation where the Dog and Cat classes exist in a third party assembly and I can't modify them. The only solution that I can come up with is:
public class Animal
{
public string Colour { get; set; }
public int Weight { get; set; }
public Animal(Cat data){Init(data);}
public Animal(Dog data){Init(data);}
private void Init(dynamic data)
{
this.Colour = data.Colour;
this.Weight = data.Weight;
}
}
This works but I lose all type safety, is there a better solution than constructor injection?
Thanks,
Joe
EDIT: Here is a real world example. I have a third party library which returns 3 objects called:
GetPageByIdResult
GetPagesByParentIdResult
GetPagesByDateResult
(These are all auto generated classes from a service reference and the properties are pretty much identical)
Instead of dealing with these three objects I want to deal with a single PageData object or a collection of them.
You can have the logic in one common constructor that all the other constructors call:
public class Animal
{
public string Colour { get; set; }
public int Weight { get; set; }
public Animal(Dog data) : this (data.Colour, data.Weight)
{
}
public Animal(Cat data) : this (data.Colour, data.Weight)
{
}
private Animal(string colour, int weight)
{
this.Colour = colour;
this.Weight = weight;
}
}
This is pretty similar to your second solution but it doesn't lose type safety.
I'm currently in a situation where the Dog and Cat classes exist in a
third party assembly and I can't modify them
I'd suggest Automapper-based solution:
public static class AnimalFactory
{
public static Animal Create<T>(T source)
where T : class
{
Mapper.CreateMap<T, Animal>();
return Mapper.Map<Animal>(source);
}
}
Usage:
var catAnimal = AnimalFactory.Create(cat);
var dogAnimal = AnimalFactory.Create(dog);
Of course, you can provide a way to custom mapping configuration, if needed.
If you do not want to have the class littered like that you can try Extension methods?
public static Animal ToAnimal(this Dog item)
{
return new Animal() {Weight = item.Weight, Colour = item.Colour};
}
public static Animal ToAnimal(this Cat item)
{
return new Animal() {Weight = item.Weight, Colour = item.Colour};
}
try using json serializer's, with that we can ensure type safety.
public class Animal
{
public string Colour { get; set; }
public long Weight { get; set; }
public string Name { get; set; }
public Animal Create<T>(T anyType)
{
return GetObject<T, Animal>(anyType);
}
public K GetObject<T, K>(T type1)
{
try
{
var serialized = JsonConvert.SerializeObject(type1);
return JsonConvert.DeserializeObject<K>(serialized);
}
catch (Exception ex)
{
return default(K);
}
}
}
class Program
{
public static void Main(string[] args)
{
Animal obj = new Animal();
var animal = obj.Create(new { Colour = "Red", Weight = 100 });
//here you can pass any object, only same name properties will be initialized..
Console.WriteLine(animal.Colour + " : " + animal.Weight);
Console.ReadKey();
}
}
I have the following interface:
public interface IObject{
double x {get;}
double y {get;}
List<IObject> List{get; set;}
}
and this class
public class Holder<T> where T : IObject {
private T myItem;
public void ChangeItemList(T item){
myItem.List = item.List;
}
However the compiler doesn't like the ChangeItemList method and on this line :
myItem.List = item.List;
gives me this error:
Cannot convert source type 'List<T>' to target type 'List<IObject>'
Why can't I do it and what is a good solution for this scenario?
thank you
I am not sure what you want to achieve but the following compiles and runs without exceptions:
class Program
{
static void Main(string[] args)
{
var holder = new Holder<IObject>();
holder.MyItem = new Object { List = new List<IObject>() };
holder.ChangeItemList(new Object { List = new List<IObject>() });
}
}
public class Object : IObject
{
public List<IObject> List { get; set; }
}
public interface IObject
{
List<IObject> List { get; set; }
}
public class Holder<T> where T : IObject
{
public T MyItem { get; set; }
public void ChangeItemList(T item)
{
MyItem.List = item.List;
}
}
Try to do this one- worked for me. The problem i had that myItem was null.
public class Holder<T> where T : IObject
{
private T myItem = Activator.CreateInstance<T>();
public void ChangeItemList(T item)
{
myItem.List = item.List;
}
}
I am attempting to use ProtoBuf net to serialize an object tree with the classes in the following format:
[ProtoContract]
class MySpecialCollectionList<T> : List<MySpecialCollection<T>>
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class MySpecialCollection<T> : List<Special<T>>
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Special<T>
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public string Description { get; set; }
[ProtoMember(3)]
private readonly T _source;
T Source { get { return _source; } }
private Special()
{
}
public Special(T source)
{
_source = source;
}
}
interface IBeast
{
string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
[ProtoMember(1)]
public string Name { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
using (var fs = File.Create(#"c:\temp\protobuftest.bin"))
{
Serializer.Serialize(fs, collectionList);
fs.Close();
}
}
private MySpecialCollectionList<IBeast> GetSpecialCollectionList()
{
var ant = new Ant() { Name = "Mr Ant" };
var cat = new Cat() { Name = "Mr Cat" };
var dog = new Dog() { Name = "Mr Dog" };
var Special = new Special<IBeast>(ant);
var specialCollection1 = new MySpecialCollection<IBeast>() {
{new Special<IBeast>(ant)},
{new Special<IBeast>(cat)},
{new Special<IBeast>(dog)}
};
specialCollection1.Name = "Special Collection1";
var specialCollection2 = new MySpecialCollection<IBeast>() {
{new Special<IBeast>(ant)},
{new Special<IBeast>(dog)}
};
specialCollection2.Name = "Special Collection2";
var specialCollectionList = new MySpecialCollectionList<IBeast>() {
specialCollection1, specialCollection2 };
specialCollectionList.Name = "Special Collection List";
return specialCollectionList;
}
}
Notice how the class I am serializing (MySpecialCollectionList<T>) is derived from a List<SomeOtherClass<T>>, not just List<T>.
I am struggling to work out where to put "ProtoInclude" attributes to get this to serialize all the items in the MySpecialCollectionList. Any help would be much appreciated.
Inheritance is not an issue here since even if A : B it is not true that Foo<A> : Foo<B>. Note that protobuf-net won't use a non-default constructor, although it is possible to skip the constructor, binding to the field directly (even readonly). While you may have 6 T, I can't see (from the code) that it would ever be in doubt which closed type you intend, and if the closed type is known you should be set.
If you have a Foo<SomeBaseClass> and a number of concrete types inherited from SomeBaseClass then the markers would o on SomeBaseClass.
However, if you have a concrete scenario I can use to reproduce your issue, I'll happily take a look.
Updated re edit:
There are a couple of key points drawn out in the example:
in common with most binding APIs, XmlSerializer and IIRC DataContractSerializer, an item is either a list xor an item with values; if a collection (something implementing IList) has properties itself, they will not be serialized; encapsulation is preferred over inheritance here, i.e. something that has a Name and has a list (rather than has a Name and is a list)
protobuf-net v1 does not support interface-based serialization; v2 does, but as with XmlSerializer and DataContractSerializer you need to explicitly tell it what things it needs to expect; quite nicely, though, we can move the [ProtoMember] onto the interface itself
Here's a fully working version in v2:
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
class MySpecialCollectionList<T>
{
[ProtoMember(1)]
public string Name { get; set; }
private readonly List<MySpecialCollection<T>> items = new List<MySpecialCollection<T>>();
[ProtoMember(2)]
public List<MySpecialCollection<T>> Items { get { return items; } }
}
[ProtoContract]
class MySpecialCollection<T>
{
[ProtoMember(1)]
public string Name { get; set; }
private readonly List<Special<T>> items = new List<Special<T>>();
[ProtoMember(2)]
public List<Special<T>> Items { get { return items; } }
}
[ProtoContract]
class Special<T>
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public string Description { get; set; }
[ProtoMember(3)]
private readonly T _source;
T Source { get { return _source; } }
private Special()
{
}
public Special(T source)
{
_source = source;
}
}
[ProtoContract]
[ProtoInclude(2, typeof(Ant))]
[ProtoInclude(3, typeof(Cat))]
[ProtoInclude(4, typeof(Dog))]
interface IBeast
{
[ProtoMember(1)]
string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
public string Name { get; set; }
}
public static class Form1
{
private static void Main()
{
MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
var copy = Serializer.DeepClone(collectionList);
}
private static MySpecialCollectionList<IBeast> GetSpecialCollectionList()
{
var ant = new Ant() { Name = "Mr Ant" };
var cat = new Cat() { Name = "Mr Cat" };
var dog = new Dog() { Name = "Mr Dog" };
var Special = new Special<IBeast>(ant);
var specialCollection1 = new MySpecialCollection<IBeast>() {Items =
{new Special<IBeast>(ant),
new Special<IBeast>(cat),
new Special<IBeast>(dog)}
};
specialCollection1.Name = "Special Collection1";
var specialCollection2 = new MySpecialCollection<IBeast>()
{
Items =
{new Special<IBeast>(ant),
new Special<IBeast>(dog)}
};
specialCollection2.Name = "Special Collection2";
var specialCollectionList = new MySpecialCollectionList<IBeast>()
{
Items ={
specialCollection1, specialCollection2 }
};
specialCollectionList.Name = "Special Collection List";
return specialCollectionList;
}
}
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
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
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;
}
}
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;
}
}
}