You, probably, have one very common myth rooted in your head that type-values are stored on the stack. In fact, this is not always the case. Only the method parameters and local variables of these methods are stored in the stack (of course, we are talking about value types. Reference types are always stored on the heap). That is, if a certain class has a field of type int , then this field will be stored on the heap along with all other data of this class (by the way the same applies to arrays - the contents of the array int [] will also be stored on the heap). Of course, it follows from this that value types can be stored in a heap, and this happens much more often than it might seem at first glance.
However, this information is, of course, important and curious, but it has no direct relation to your question.
Now for the actual issue. Type design
var foo = new Foo()
or rather, the new keyword does not mean at all that memory is allocated exactly on the heap, and specifically for the reference type (unlike C ++, where new means just the return of the pointer). Here it is important to remember that new is just a part of the syntax of the language, and by itself this word implies just allocating memory for an object of type Foo with a call to its constructor, without reference to whether the reference is a type or type-value.
By the way, besides the above, the CLR has another way to store type-values in a heap. This can be achieved by packing, that is, by casting an instance of a value type to a reference type, which also means storing it on the heap. However, this is a separate and quite extensive topic.
ZY one more small detail, which, however, is hardly useful to you - even local variables related to value types can be stored on the heap if we are talking about anonymous methods and lambda functions. Storing them in a heap in this case makes sense to implement closures.