Populating the wrapped <a> collection with wrapped <b> classes, where B implements A
So, I have the following simple wrapper class:
interface IReference<out T> where T : myAbstractBase { T Value { get; } } public class Reference<T> : IReference<T> where T : myAbstractBase { private T _value = null; public T Value { get { return _value; } } } In my application, I want to have collections of these IReference<someClass> objects (where someClass implements myAbstractBase )
private List<Reference<shapes>> shapeList = new Collection<Reference<shapes>>(); But I want to be able to add all kinds of different shapes to this collection. (Moreover, the form is also abstract). Of course, this gives an error:
shapeList.Add( new Reference<circle>(){ radius = 2; } ); The value "Reference[circle]" is not of type "Reference[shape]" and cannot be used in this generic collection.
Is there any way to create my Reference<T> class in such a way that as long as A is of type B , Reference<A> will consider the type of Reference<B> ?
It seems to me that people have faced the same problem trying to use Nullable lists, etc.
I tried to implement implicit operators to convert between Reference and T, but I did not think about practical use for them ...
public class Reference<T> ... { ... public static implicit operator Reference<T>(T value) { return new Reference<T> { _value = value, }; } public static implicit operator T(Reference<T> value) { return value.Value; } } For anyone interested in my intentions, all this is part of a (ill-fated) attempt to implement lazy loading for a set of classes without the need to add anything else to these classes.
I rephrased the question and got the answer in another thread: How to distinguish Generic <T> to Generic <R> where T is a subclass of R?
The trick was to create a covariant IReference<out T> interface and always use the interface wherever this type is used. So, declare List<IReference<myAbstractBase>> , not List<Reference<myAbstractBase>> . Details of why this works in covariance and contraception in general
Your problem is that you cannot associate user-defined implicit drops. At first glance it seems that you can go from Reference<Circle> β Reference<Shape> via Reference<Circle> β Circle β Shape β Reference<Shape> . However, you will use two custom implicit translations. First you go from Reference<Circle> β Circle through operator T(Reference<T> value) . Then you should go from Shape β Reference<Shape> via operator Reference<T>(T value) . You can get around this by creating an overload of the Add method by expanding the List . This will allow you to explicitly use one of the user-defined translation operators in Reference.Add . Now you donβt have to bind custom implicit translation operators.
See the specification for custom implicit broadcasts: http://msdn.microsoft.com/en-us/library/aa691302(v=vs.71).aspx
//You can get around your inability to chain user defined implicit casts //by creating a ReferenceList<T> that extends List<IReference<T>> //and overloads the List.Add method public class ReferenceList<T> : List<IReference<T>> where T : MyAbstractBase { //With this overload you can accept a T. Then explicity cast to Reference<T> //by using operator Reference<T>(T value) public void Add(T item) { base.Add((Reference<T>)item); } } List<Reference<Shape>> shapeList = new List<Reference<Shape>>(); ReferenceList<Shape> shapeList2 = new ReferenceList<Shape>(); List<IReference<Shape>> shapeList3 = new List<IReference<Shape>>(); //Interesting cases that should work with the OP //Works for obvious reasons shapeList.Add(new Reference<Shape>()); //Works because you're using one user defined implicit cast //where the cast is operator Reference<T>(T value). //Shape -> Reference<Shape> shapeList.Add(new Shape()); //Works because you're using one non user defined implicit cast and one user defined //implicit cast where the user defined implicit cast is operator Reference<T>(T value) //Circle -> Shape -> Wrapper<Shape> shapeList.Add(new Circle()); //Does not work because you need to chain two user defined implicit casts //where the implicit casts are operator T(Reference<T> value) and operator Reference<T>(T value) //Reference<Circle> -> Circle -> Shape -> Reference<Shape> //Theoretically this could work, but the C# specs state that chaining user defined //implicit casts is not allowed in C# (See link below) shapeList.Add(new Reference<Circle>()); //This case works for similiar reasons that shapeList.Add(new Circle()). It uses //only one user defined implicit cast because you're calling operator T(Reference<T> value) //explicitely shapeList.Add(new (Circle)Reference<Circle>()); //Interesting cases for ReferenceList //Works because this calls List.Add which accepts a Reference<T> shapeList2.Add(new Reference<Shape>()); //Works because this calls ReferenceList.Add wich accepts a T shapeList2.Add(new Circle()); //Works because this calls ReferenceList.Add wich accepts a T. //and Reference<Circle> can be implicitly cast to a Circle via //operator T(Reference<T> value). //Reference<Circle> -> Circle -> Shape -> Reference<Shape> where //the last cast is done explicitely in the ReferenceList.Add method //via operator Reference<T>(T value) shapeList2.Add(new Reference<Circle>()); //Interesting cases for List<IReference<Shape>> //Works for obvious reasons shapeList3.Add(new Reference<Shape>()); //Works because IReference is covariant. In C# interfaces can be //covariant. Classes cannot be covariant. shapeList3.Add(new Reference<Circle>()); //Does not work because C# does not support user defined implicit //casts to interface. In other words, you implicitly cast Shape -> Reference<Shape> shapeList3.Add(new Shape()); //Doesn't work for similiar reasons to why shapeList3.Add(new Shape()) doesn't work shapeList3.Add(new Circle()); Well, here's a shot in the dark that should at least make your implicit statement compile. I have nothing to check immediately. However, this should work. Add this to the Reference class.
public static implicit operator Reference<myAbstractBase>(Reference<T> i) { return i; } Note that there is no type checking here, so if T is not derived from myAbstractBase, you can be closed.
I started the path of defining a custom collection, which is just List<Reference<T>> inside, but seems to do the trick:
public class ReferenceCollection<T> : ICollection<T> where T : myAbstractBase { private List<Reference<T>> collection = new List<Reference<T>>(); public IEnumerable<T> toIEnumerable() { return (IEnumerable<T>) collection.Select(r => r.Value); } public IEnumerator<T> GetEnumerator() { return toIEnumerable().GetEnumerator(); ; } #region ICollection<T> Members public void Add(T item) { collection.Add(item); } ... } Now I can make a folling call (which I could not do before):
ReferenceCollection<shape> test = new ReferenceCollection<shape>(); test.Add(new circle()); Why does it work this way, but not another? Am I really doing something completely different? Perhaps the conversion actually happens in the reverse order (the circle becomes different for the form, and then the form gets implicitly converted to Resource<shape> as it is added to the internal collection.)
I do not see any obstacles for this method yet. I could even define implicit converters to turn a ReferenceCollection<T> directly into a List<T> or List<Reference<T>> without as much as iterating over it. However, I wonder if there is a way to define the source class in such a way that the cast happens in the same way before the implicit conversion to avoid a non-blocking wrapped type.