B
B
BadCats2016-07-08 16:23:28
C++ / C#
BadCats, 2016-07-08 16:23:28

Generic Types and Double UpCast?

Hello everyone, I have the following example:

public abstract class Shape { }
    public class Circle : Shape { }

    public interface IContainer<out T>
    {
        T Figure { get; }
    }

    public class Container<T> : IContainer<T>
    {
        private T figure;

        public Container(T figure)
        {
            this.figure = figure;
        }

        public T Figure
        {
            get { return figure; }
        }
    }

    class Program
    {
        static void Main()
        {
            Circle circle = new Circle();

            IContainer<Shape> container = new Container<Circle>(circle);

            Console.WriteLine(container.Figure.ToString());

            // Delay.
            Console.ReadKey();
        }
    }

I'm interested in this line
IContainer<Shape> container = new Container<Circle>(circle);

- an instance of circle (passed as a constructor argument) is not cast to any type in this case - because the type is T (I know it's correct to say the type of placeholder is type T, but just "type T" would be shorter) of the class We closed the Container with the same type as this instance (and I mean that since they have the same types, there is nothing to result in.)
So, we first cast the Container type to the IContainer type, and the figure field of the type Circle is cast to the Shape type.
Why is the figure field inside the Container class cast to a Shape?
so my assumptions:
1) the instance passed as an argument has nothing to do with it - because, as already mentioned above, the instance itself is not given to anything.
2) Due to the fact that we brought the Container type to the IContainer type - the type T of which we closed with the Shape type
- and this raises another question: we have two UpCasts happening at the same time: Container - IContainer;
circle shape; - which of them, let's say, influence the passed constructor argument - our circle instance (yes, yes, I know that I wrote above that UpCast -a does not occur on the circle instance - just in the previous example, the instance is cast to the Shape type)
IContainer<Shape> container = new Container<Shape>(circle);

- but, since the figure field changes type, and a reference to this field is stored in this instance, which we pass as a constructor argument.
My guess is that since we type T from Container (Circle) - the type of which and the argument of the circle constructor - cast to type T from IContainer (Shape), then this constructor argument of type Circle is also implicitly UpCast - it is "following" changes in its type - and accordingly then there will be an UpCast of types inside this instance, including the figure field - which will change its Circle type to Shape.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
S
Stanislav Makarov, 2016-07-08
@BadCats

You don't quite understand covariance in generic interfaces and you've got a lot of things mixed up.
First, the type of the figure field does not change to any other. It cannot change "on the fly", because. a concrete object of a concrete class has already been created.
The return type of the Figure property also does not change.
I repeat once again - the class and its instance do not change any types of either fields or properties.
For some reason, you perceive the cast of a class type to an interface as a conversion of the value of an object - as if another Circle will be created, which will have other types of fields. It is absolutely not true.
When it comes to reference types (and polymorphism using interfaces only works with such types), then you should clearly understand that you are working with a reference, and type casting for a reference cannot affect the object itself in any way. In other words, you simply change the "window" through which you look at the object.
So covariance in the case of interfaces is purely a matter of typing. When co- and contravariance was added to C#, in fact, you were allowed to consider that IContainer is a special case of IContainer, and if so, ANY IContainer can be treated as IContainer. In earlier versions of the language, this could not be done - only Container could be treated as IContainer.
If something is not clear in the answer, ask, because Your question sounds like you have a big gap in your understanding of the language somewhere. You dig into the details, making completely wrong assumptions about what is happening.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question