-3

I have two classes:

public class Asset {  }

public class Stock : Asset 
{
    ...
}

When I write:

Stock m = new Stock ();
Asset а = m;             
Stock s = (Stock) а; 

Everything works fine!

But when I write in this way:

Asset а = new Asset();             
Stock s = (Stock) а;

result is InvalidCastException, why?

Edward0802
  • 13
  • 1
  • 1
    https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/ – Bmo Oct 20 '17 at 17:57
  • 1
    I'm no C# guru, but it looks like you have a typo on your second example: "Asset а = new Assert();" There is an extra "r" in your Asset. – bakoyaro Oct 20 '17 at 18:12
  • Why is Asset even concrete in the first place? Surely it ought to be abstract. – Eric Lippert Oct 20 '17 at 20:46

2 Answers2

2

In your first instance, a is a reference of type Asset, but it's referring to an object whose actual runtime type is Stock.

Stock m = new Stock ();
Asset а = m;             
Stock s = (Stock) а; 

m, a, and s are all different references to the same actual object, whose type is Stock.

Here, the actual object not a Stock. It's just an Asset:

Asset а = new Assert();             
Stock s = (Stock) а;

Because a Stock inherits from Asset, it is a superset of Asset. You can pretend a Stock is an Asset and that's fine, because Stock is, in part, an Asset -- plus whatever else it adds on its own. Naturally, that doesn't cut both ways: An Asset doesn't have all the stuff Stock has, so you can't treat it as if it were a Stock.

Your assignment to a in the first example didn't change the object into an Asset, or create a new Asset and assign it to m. It was still the same object.

The type of the reference is only part of the story.

Try this:

Stock m = new Stock ();
Asset а = m;             

//  This will print "Stock" -- it's still that same actual Stock object. 
Console.WriteLine(a.GetType());

Stock s = (Stock) а; 

Classes are "reference types". They exist on "the heap", out in the darkness somewhere, and you only ever manipulate references to them. Integers and doubles are different. They're "value types":

int n = 4;
double d = (double)n;

That code actually creates a new double, equal to 4.0. d doesn't "refer to" n; it is its own, different value. That's very different from how reference types work.

This stuff is a fundamental feature of the .NET type system. A struct (for example DateTime) is also a value type.

1

A stock is an asset. An asset is not a stock.

Say you have 3 methods in your Asset base class. These are also present in Stock. Hence when you cast a Stock to an Asset you are in fact putting an restriction on Stock. Your object, which is a Stock, is told to be an Asset. This it knows how to do since it is an Asset.

The stock might have 3 methods of its own that are not part of the base Asset class. When you tell an Asset to be an Stock it wont know how to do it, since there are parts of Stock that are not part of Asset.

Alex Telon
  • 1,107
  • 1
  • 14
  • 30