Полезно рассматривать .net как имеющий три «безопасных» типа типов в .net: интерфейсы, классы и типы значений (есть также такие вещи, как указатели и тому подобное, но это другая история) и три основных Могут использоваться классы контекстов: места хранения, объекты кучи и общие ограничения.
Объекты кучи могут быть любого типа, но все объекты кучи ведут себя как объекты класса. Объекты кучи типа интерфейса встречаются редко; они обычно не создаются в .net, но могут создаваться кодом, предназначенным для работы со старыми объектными моделями. Объекты кучи типов классов содержат одно хранилище для каждого поля; Объекты кучи типов значений содержат одно место хранения, тип которого является типом значения, о котором идет речь.
Места хранения также могут быть любого типа, но места хранения ценностного типа отличаются от других. Место хранения типа класса или типа интерфейса содержит ссылку на класс. Место хранения типа значения содержит либо примитив значения (байт, целое число, символ, число с плавающей точкой и т. Д.), Либо содержит место хранения для каждого поля типа значения (например, место хранения типа *). 1005 * содержит два хранилища типа Int32
, каждое из которых содержит подписанный 32-битный целочисленный примитив).
Общие ограничения также могут быть любого типа, но ограничения типов интерфейса не ограничивают сам параметр ограниченного универсального типа как тип класса, тип интерфейса или тип значения. Метод, объявленный void Foo<T>(T param) where T:IWowzo
, может быть вызван с параметром типа класса, типа интерфейса или типа значения. Если подпрограмма вызывается с параметром типа значения, то param
и любые другие места хранения, объявленные как тип T
, будут сохранены как типы значений. Если подпрограмма вызывается с параметром типа класса или целочисленного типа, то param
и любые другие места хранения, объявленные как тип T
, будут сохранены как ссылки на классы. Важно отметить, что если T
сам по себе является типом интерфейса (IWozo
или производным), то param
будет передан как ссылка на объект кучи и будет вести себя как единое целое независимо от того, является ли тип объекта Экземпляр - это объект класса или тип значения. Если struct Bar
реализует IWowzo
, а myBar
- это переменная типа Bar
, вызов Foo<Bar>(myBar)
может дать семантику, отличную от Foo<IWowzo>(myBar)
; в первом случае параметр будет вести себя как тип значения, а во втором - как тип класса.