This is a classical class hierarchy, which so far does not do anything concrete, but we don’t need it now. Now we define a dummy class, which can be a container for any type.public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .
public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .
public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .
public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .
public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .
public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .
public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .
public class Shape { } public class Circle : Shape { } * This source code was highlighted with Source Code Highlighter .
We have a hierarchy and a container. Now let's take a look at what we can’t do in the current version of C # - 3.0.* This source code was highlighted with Source Code Highlighter .
- public interface IContainer <T>
- {
- T GetItem ();
- }
- public class Container <T>: IContainer <T>
- {
- private T item;
- public Container (T item)
- {
- this .item = item;
- }
- public T GetItem ()
- {
- return item;
- }
- }
We have a GetList method, in which the return type is defined as IContainer <Shape>, and it returns the Container <Circle>. Since Circle is inherited from Shape, and Container implements the IContainer interface, this may seem to work. But, you guessed it, C # 3.0 is not capable of this.* This source code was highlighted with Source Code Highlighter .
- static void Main ( string [] args)
- {
- IContainer <Shape> list = GetList ();
- }
- public static IContainer <Shape> GetList ()
- {
- return new Container <Circle> ( new Circle ());
- }
This construct tells the compiler that type T is covariant, which means that any IContainer <T> will accept any type equivalent or more specific than T. As we saw above, the return value type was IContainer <Shape>, but if we put the out parameter to our interface, we can easily return IContainer <Circle> as well. So why did you decide to use the out keyword? This is because when you define a parameter-type as covariant you can only return this type from the interface. For example, such a construction is not allowed:* This source code was highlighted with Source Code Highlighter .
- public interface IContainer < out T>
- {
- T GetItem ();
- }
But why won't it work? The answer is really very simple - type safety. Let's look at the consequences of what we did:* This source code was highlighted with Source Code Highlighter .
- public interface IContainer < out T>
- {
- void SetItem (T item);
- T GetItem ();
- }
Since T is covariant and, therefore, we can assign a Container <Circle> to a variable of type IContainer <Shape>, passing it further to our static method SetItem, which accepts a parameter of type IContainer <Shape> and then we take this parameter and try to add variable of type Square into it. It seems that everything is correct - the type of the IContainer <Shape> parameter and this gives us the right to add Square to it. Wrong. This expression will “explode” as we try to add Square to the container that the Circle holds. Therefore, with the out keyword, they limited the covariance to only one direction.* This source code was highlighted with Source Code Highlighter .
- static void Main ( string [] args)
- {
- IContainer <Shape> container = new Container <Circle> ();
- SetItem (container);
- }
- public static void SetItem (IContainer <Shape> container)
- {
- container.SetItem ( new Square ()); // BOOM !!!
- }
Source: https://habr.com/ru/post/43938/
All Articles