Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I had to think long and hard on how to explain this well. Explaining is seems to be just as hard as understanding it.</p> <p>Imagine you have a base class Fruit. And you have two subclasses Apple and Banana. </p> <pre><code> Fruit / \ Banana Apple </code></pre> <p>You create two objects:</p> <pre><code>Apple a = new Apple(); Banana b = new Banana(); </code></pre> <p>For both of these objects you can typecast them into the Fruit object.</p> <pre><code>Fruit f = (Fruit)a; Fruit g = (Fruit)b; </code></pre> <p>You can treat derived classes as if they were their base class.</p> <p>However you cannot treat a base class like it was a derived class</p> <pre><code>a = (Apple)f; //This is incorrect </code></pre> <p>Lets apply this to the List example.</p> <p>Suppose you created two Lists:</p> <pre><code>List&lt;Fruit&gt; fruitList = new List&lt;Fruit&gt;(); List&lt;Banana&gt; bananaList = new List&lt;Banana&gt;(); </code></pre> <p>You can do something like this...</p> <pre><code>fruitList.Add(new Apple()); </code></pre> <p>and</p> <pre><code>fruitList.Add(new Banana()); </code></pre> <p>because it is essentially typecasting them as you add them into the list. You can think of it like this...</p> <pre><code>fruitList.Add((Fruit)new Apple()); fruitList.Add((Fruit)new Banana()); </code></pre> <p>However, applying the same logic to the reverse case raises some red flags.</p> <pre><code>bananaList.Add(new Fruit()); </code></pre> <p>is the same as</p> <pre><code>bannanaList.Add((Banana)new Fruit()); </code></pre> <p>Because you cannot treat a base class like a derived class this produces errors. </p> <p>Just in case your question was why this causes errors I'll explain that too.</p> <p>Here's the Fruit class</p> <pre><code>public class Fruit { public Fruit() { a = 0; } public int A { get { return a; } set { a = value } } private int a; } </code></pre> <p>and here's the Banana class</p> <pre><code>public class Banana: Fruit { public Banana(): Fruit() // This calls the Fruit constructor { // By calling ^^^ Fruit() the inherited variable a is also = 0; b = 0; } public int B { get { return b; } set { b = value; } } private int b; } </code></pre> <p>So imagine that you again created two objects</p> <pre><code>Fruit f = new Fruit(); Banana ba = new Banana(); </code></pre> <p>remember that Banana has two variables "a" and "b", while Fruit only has one, "a". So when you do this...</p> <pre><code>f = (Fruit)b; f.A = 5; </code></pre> <p>You create a complete Fruit object. But if you were to do this...</p> <pre><code>ba = (Banana)f; ba.A = 5; ba.B = 3; //Error!!!: Was "b" ever initialized? Does it exist? </code></pre> <p>The problem is that you don't create a complete Banana class.Not all the data members are declared / initialized.</p> <p>Now that I'm back from the shower and got my self a snack heres where it gets a little complicated.</p> <p>In hindsight I should have dropped the metaphor when getting into the complicated stuff</p> <p>lets make two new classes:</p> <pre><code>public class Base public class Derived : Base </code></pre> <p>They can do whatever you like</p> <p>Now lets define two functions</p> <pre><code>public Base DoSomething(int variable) { return (Base)DoSomethingElse(variable); } public Derived DoSomethingElse(int variable) { // Do stuff } </code></pre> <p>This is kind of like how "out" works you should always be able to use a derived class as if it were a base class, lets apply this to an interface</p> <pre><code>interface MyInterface&lt;T&gt; { T MyFunction(int variable); } </code></pre> <p>The key difference between out/in is when the Generic is used as a return type or a method parameter, this the the former case.</p> <p>lets define a class that implements this interface:</p> <pre><code>public class Thing&lt;T&gt;: MyInterface&lt;T&gt; { } </code></pre> <p>then we create two objects: </p> <pre><code>MyInterface&lt;Base&gt; base = new Thing&lt;Base&gt;; MyInterface&lt;Derived&gt; derived = new Thing&lt;Derived&gt;; </code></pre> <p>If you were do this:</p> <pre><code>base = derived; </code></pre> <p>You would get an error like "cannot implicitly convert from..."</p> <p>You have two choices, 1) explicitly convert them or, 2) tell the complier to implicitly convert them.</p> <pre><code>base = (MyInterface&lt;Base&gt;)derived; // #1 </code></pre> <p>or </p> <pre><code>interface MyInterface&lt;out T&gt; // #2 { T MyFunction(int variable); } </code></pre> <p>The second case comes in to play if your interface looks like this:</p> <pre><code>interface MyInterface&lt;T&gt; { int MyFunction(T variable); // T is now a parameter } </code></pre> <p>relating it to the two functions again</p> <pre><code>public int DoSomething(Base variable) { // Do stuff } public int DoSomethingElse(Derived variable) { return DoSomething((Base)variable); } </code></pre> <p>hopefully you see how the situation has reversed but is essentially the same type of conversion.</p> <p>Using the same classes again</p> <pre><code>public class Base public class Derived : Base public class Thing&lt;T&gt;: MyInterface&lt;T&gt; { } </code></pre> <p>and the same objects</p> <pre><code>MyInterface&lt;Base&gt; base = new Thing&lt;Base&gt;; MyInterface&lt;Derived&gt; derived = new Thing&lt;Derived&gt;; </code></pre> <p>if you try to set them equal</p> <pre><code>base = derived; </code></pre> <p>your complier will yell at you again, you have the same options as before</p> <pre><code>base = (MyInterface&lt;Base&gt;)derived; </code></pre> <p>or</p> <pre><code>interface MyInterface&lt;in T&gt; //changed { int MyFunction(T variable); // T is still a parameter } </code></pre> <p>Basically use out when the generic is only going to be used as a return type of the interface methods. Use in when it is going to be used as a Method parameter. The same rules apply when using delegates too.</p> <p>There are strange exceptions but I'm not going to worry about them here.</p> <p>Sorry for any careless mistakes in advance =)</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload