Template class t c что это
Кроме обычных типов фреймворк .NET также поддерживает обобщенные типы (generics), а также создание обобщенных методов. Чтобы разобраться в особенности данного явления, сначала посмотрим на проблему, которая могла возникнуть до появления обобщенных типов. Посмотрим на примере. Допустим, мы определяем класс для хранения данных пользователя:
class Person < public int Id < get;>public string Name < get;>public Person(int id, string name) < Name = name; >>
Класс Person определяет два свойства: Id — уникальный идентификатор пользователя и Name — имя пользователя.
Здесь идентификатор пользователя задан как числовое значение, то есть это будут значения 1, 2, 3, 4 и так далее.
Однако также нередко для идентификатора используются и строковые значения. И у числовых, и у строковых значений есть свои плюсы и минусы. И на момент написания класса мы можем точно не знать, что лучше выбрать для хранения идентификатора — строки или числа. Либо, возможно, этот класс будет использоваться другими разработчиками, которые могут иметь свое мнение по данной проблеме, например, они могут для представления идентификатора создать специальный класс.
И на первый взгляд, чтобы выйти из подобной ситуации, мы можем определить свойство Id как свойство типа object. Так как тип object является универсальным типом, от которого наследуется все типы, соответственно в свойствах подобного типа мы можем сохранить и строки, и числа:
class Person < public object Id < get;>public string Name < get;>public Person(object id, string name) < Name = name; >>
Затем этот класс можно было использовать для создания пользователей в программе:
Person tom = new Person(546, "Tom"); Person bob = new Person("abc123", "Bob"); int tomId = (int)tom.Id; string bobId = (string) bob.Id; Console.WriteLine(tomId); // 546 Console.WriteLine(bobId); // abc123
Все вроде замечательно работает, но такое решение является не очень оптимальным. Дело в том, что в данном случае мы сталкиваемся с такими явлениями как упаковка (boxing) и распаковка (unboxing) .
Так, при передаче в конструктор значения типа int, происходит упаковка этого значения в тип Object:
Person tom = new Person(546, "Tom"); // упаковка в значения int в тип Object
Чтобы обратно получить данные в переменную типов int, необходимо выполнить распаковку:
int tomId = (int)tom.Id; // Распаковка в тип int
Упаковка (boxing) предполагает преобразование объекта значимого типа (например, типа int) к типу object. При упаковке общеязыковая среда CLR обертывает значение в объект типа System.Object и сохраняет его в управляемой куче (хипе). Распаковка (unboxing), наоборот, предполагает преобразование объекта типа object к значимому типу. Упаковка и распаковка ведут к снижению производительности, так как системе надо осуществить необходимые преобразования.
Кроме того, существует другая проблема — проблема безопасности типов. Так, мы получим ошибку во время выполнения программы, если напишем следующим образом:
Person tom = new Person(546, "Tom"); string tomId = (string)tom.Id; // !Ошибка - Исключение InvalidCastException Console.WriteLine(tomId); // 546
Мы можем не знать, какой именно объект представляет Id, и при попытке получить число в данном случае мы столкнемся с исключением InvalidCastException. Причем с исключением мы столкнемся на этапе выполнения программы.
Для решения этих проблем в язык C# была добавлена поддержка обобщенных типов (также часто называют универсальными типами). Обобщенные типы позволяют указать конкретный тип, который будет использоваться. Поэтому определим класс Person как обощенный:
class Person < public T Id < get; set; >public string Name < get; set; >public Person(T id, string name) < Name = name; >>
Угловые скобки в описании class Person указывают, что класс является обобщенным, а тип T, заключенный в угловые скобки, будет использоваться этим классом. Необязательно использовать именно букву T, это может быть и любая другая буква или набор символов. Причем сейчас на этапе написания кода нам неизвестно, что это будет за тип, это может быть любой тип. Поэтому параметр T в угловых скобках еще называется универсальным параметром , так как вместо него можно подставить любой тип.
Например, вместо параметра T можно использовать объект int, то есть число, представляющее номер пользователя. Это также может быть объект string, либо или любой другой класс или структура:
Person tom = new Person(546, "Tom"); // упаковка не нужна Person bob = new Person("abc123", "Bob"); int tomId = tom.Id; // распаковка не нужна string bobId = bob.Id; // преобразование типов не нужно Console.WriteLine(tomId); // 546 Console.WriteLine(bobId); // abc123
Поскольку класс Person является обобщенным, то при определении переменной после названия типа в угловых скобках необходимо указать тот тип, который будет использоваться вместо универсального параметра T. В данном случае объекты Person типизируются типами int и string :
Person tom = new Person(546, "Tom"); // упаковка не нужна Person bob = new Person("abc123", "Bob");
Поэтому у первого объекта tom свойство Id будет иметь тип int, а у объекта bob — тип string. И в случае с типом int упаковки происходить не будет.
При попытке передать для параметра id значение другого типа мы получим ошибку компиляции:
Person tom = new Person("546", "Tom"); // ошибка компиляции
А при получении значения из Id нам больше не потребуется операция приведения типов и распаковка тоже применяться не будет:
int tomId = tom.Id; // распаковка не нужна
Тем самым мы избежим проблем с типобезопасностью. Таким образом, используя обобщенный вариант класса, мы снижаем время на выполнение и количество потенциальных ошибок.
При этом универсальный параметр также может представлять обобщенный тип:
// класс компании class Company < public P CEO < get; set; >// президент компании public Company(P ceo) < CEO = ceo; >> class Person < public T Id < get;>public string Name < get;>public Person(T id, string name) < Name = name; >>
Здесь класс компании определяет свойство CEO, которое хранит президента компании. И мы можем передать для этого свойства значение типа Person, типизированного каким-нибудь типом:
Person tom = new Person(546, "Tom"); Companymicrosoft = new Company (tom); Console.WriteLine(microsoft.CEO.Id); // 546 Console.WriteLine(microsoft.CEO.Name); // Tom
Статические поля обобщенных классов
При типизации обобщенного класса определенным типом будет создаваться свой набор статических членов. Например, в классе Person определено следующее статическое поле:
class Person < public static T? code; public T Id < get; set; >public string Name < get; set; >public Person(T id, string name) < Name = name; >>
Теперь типизируем класс двумя типами int и string:
Person tom = new Person(546, "Tom"); Person.code = 1234; Person bob = new Person("abc", "Bob"); Person.code = "meta"; Console.WriteLine(Person.code); // 1234 Console.WriteLine(Person.code); // meta
В итоге для Person и для Person будет создана своя переменная code.
Использование нескольких универсальных параметров
Обобщения могут использовать несколько универсальных параметров одновременно, которые могут представлять одинаковые или различные типы:
class Person < public T Id < get;>public K Password < get; set; >public string Name < get;>public Person(T id, K password, string name) < Name = name; Password = password; >>
Здесь класс Person использует два универсальных параметра: один параметр для идентификатора, другой параметр — для свойства-пароля. Применим данный класс:
Person tom = new Person(546, "qwerty", "Tom"); Console.WriteLine(tom.Id); // 546 Console.WriteLine(tom.Password);// qwerty
Здесь объект Person типизируется типами int и string. То есть в качестве универсального параметра T используется тип int , а для параметра K — тип string .
Обобщенные методы
Кроме обобщенных классов можно также создавать обобщенные методы, которые точно также будут использовать универсальные параметры. Например:
int x = 7; int y = 25; Swap(ref x, ref y); // или так Swap(ref x, ref y); Console.WriteLine($»x= y=»); // x=25 y=7 string s1 = «hello»; string s2 = «bye»; Swap(ref s1, ref s2); // или так Swap(ref s1, ref s2); Console.WriteLine($»s1= s2=»); // s1=bye s2=hello void Swap(ref T x, ref T y)
Здесь определен обощенный метод Swap, который принимает параметры по ссылке и меняет их значения. При этом в данном случае не важно, какой тип представляют эти параметры.
При вызове метода Swap типизируем его определенным типом и передаем ему соответствующие этому типу значения.
- Вопросы для самопроверки
- Упражнения
Шаблоны (C++)
Шаблоны являются основой для универсального программирования в C++. В качестве строго типизированного языка C++ требует, чтобы все переменные имели определенный тип, явно объявленный программистом или выводил компилятором. Однако многие структуры и алгоритмы данных выглядят одинаково независимо от типа, на который они работают. Шаблоны позволяют определить операции класса или функции и разрешить пользователю указать, какие типы этих операций должны работать.
Определение и использование шаблонов
Шаблон — это конструкция, которая создает обычный тип или функцию во время компиляции на основе аргументов, которые пользователь предоставляет для параметров шаблона. Например, можно определить шаблон функции следующим образом:
template T minimum(const T& lhs, const T& rhs)
Приведенный выше код описывает шаблон универсальной функции с одним параметром типа T, возвращаемое значение и параметры вызова (lhs и rhs) всех этих типов. Вы можете назвать параметр типа, который вы хотите, но по соглашению одноглавные буквы чаще всего используются. T является параметром шаблона; typename ключевое слово говорит, что этот параметр является заполнителем для типа. При вызове функции компилятор заменит каждый экземпляр T конкретным аргументом типа, заданным пользователем или выведенным компилятором. Процесс, в котором компилятор создает класс или функцию из шаблона, называется экземпляром шаблона. minimum Это экземпляр шаблона minimum .
В другом месте пользователь может объявить экземпляр шаблона, специализированного для int. Предположим, что get_a() и get_b() — это функции, возвращающие int:
int a = get_a(); int b = get_b(); int i = minimum(a, b);
Тем не менее, поскольку это шаблон функции, и компилятор может выводить тип T из аргументов a и b, можно вызвать его так же, как обычная функция:
int i = minimum(a, b);
При обнаружении последней инструкции компилятор создает новую функцию, в которой каждое вхождение T в шаблоне заменяется следующим int образом:
int minimum(const int& lhs, const int& rhs)
Правила того, как компилятор выполняет вычет типов в шаблонах функций, основаны на правилах для обычных функций. Дополнительные сведения см. в разделе «Разрешение перегрузки вызовов шаблонов функций».
Параметры типа
В приведенном minimum выше шаблоне обратите внимание, что параметр типа T не является соответствующим образом, пока он не будет использоваться в параметрах вызова функции, где добавляются квалификаторы констант и ссылочных квалификаторов.
Нет практического ограничения на количество параметров типа. Разделите несколько параметров запятыми:
template class Foo<>;
Ключевое слово class эквивалентен этому контексту typename . Вы можете выразить предыдущий пример следующим образом:
template class Foo<>;
Оператор многоточия (. ) можно использовать для определения шаблона, который принимает произвольное число параметров нулевого или более типа:
template class vtclass; vtclass < >vtinstance1; vtclass vtinstance2; vtclass vtinstance3;
Любой встроенный или определяемый пользователем тип можно использовать в качестве аргумента типа. Например, можно использовать std::vector в стандартной библиотеке для хранения переменных типа int , double std::string, const MyClass MyClass *, MyClass& и т. д. Основное ограничение при использовании шаблонов заключается в том, что аргумент типа должен поддерживать все операции, применяемые к параметрам типа. Например, если мы вызываем minimum использование MyClass , как в следующем примере:
class MyClass < public: int num; std::wstring description; >; int main() < MyClass mc1 ; MyClass mc2 ; auto result = minimum(mc1, mc2); // Error! C2678 >
Ошибка компилятора создается, так как MyClass не предоставляет перегрузку для оператора.
Нет никаких обязательных требований, что аргументы типа для любого конкретного шаблона принадлежат одной иерархии объектов, хотя можно определить шаблон, который применяет такое ограничение. Вы можете объединить объектно-ориентированные методы с шаблонами; Например, можно сохранить производный* в векторе. Обратите внимание, что аргументы должны быть указателями
vector vec; MyDerived d(3, L"back again", time(0)); vec.push_back(&d); // or more realistically: vector> vec2; vec2.push_back(make_shared());
Основные требования, std::vector которые и другие стандартные контейнеры библиотеки накладывают на элементы T , которые T можно назначить с помощью копирования и создания копирования.
Параметры, не относящиеся к типу
В отличие от универсальных типов на других языках, таких как C# и Java, шаблоны C++ поддерживают параметры, отличные от типов, также называемые параметрами значения. Например, можно указать целочисленное значение константы, чтобы указать длину массива, как и в этом примере, аналогичному классу std::array в стандартной библиотеке:
template class MyArray < T arr[L]; public: MyArray() < . >>;
Обратите внимание на синтаксис в объявлении шаблона. Значение size_t передается в качестве аргумента шаблона во время компиляции и должно быть const или выражением constexpr . Вы используете его следующим образом:
MyArray arr;
Другие типы значений, включая указатели и ссылки, могут передаваться в виде параметров, отличных от типа. Например, можно передать указатель на объект функции или функции, чтобы настроить некоторую операцию внутри кода шаблона.
Вычет типов для параметров шаблона, не относящихся к типу
В Visual Studio 2017 и более поздних версиях и в /std:c++17 режиме или более поздних версиях компилятор выводит тип аргумента шаблона, отличного от типа, объявленного с помощью auto :
template constexpr auto constant = x; auto v1 = constant; // v1 == 5, decltype(v1) is int auto v2 = constant; // v2 == true, decltype(v2) is bool auto v3 = constant; // v3 == 'a', decltype(v3) is char
Шаблоны в качестве параметров шаблона
Шаблон может быть параметром шаблона. В этом примере MyClass2 имеет два параметра шаблона: параметр typename T и параметр шаблона Arr:
template class Arr> class MyClass2 < T t; //OK Arra; U u; //Error. U not in scope >;
Так как сам параметр Arr не имеет текста, его имена параметров не требуются. На самом деле, это ошибка, чтобы ссылаться на имя типа или имена параметров класса Arr из текста MyClass2 . По этой причине имена параметров типа Arr могут быть опущены, как показано в этом примере:
template class Arr> class MyClass2 < T t; //OK Arra; >;
Аргументы шаблона по умолчанию
Шаблоны классов и функций могут иметь аргументы по умолчанию. Если у шаблона есть аргумент по умолчанию, его можно не указать при использовании. Например, шаблон std::vector имеет аргумент по умолчанию для распределителя:
template > class vector;
В большинстве случаев класс std::allocator по умолчанию является допустимым, поэтому используется вектор, как показано ниже:
vector myInts;
Но при необходимости можно указать пользовательский распределитель следующим образом:
vector ints;
При наличии нескольких аргументов шаблона все аргументы после первого аргумента по умолчанию должны иметь аргументы по умолчанию.
При использовании шаблона, параметры которого по умолчанию используются, используйте пустые угловые скобки:
template class Bar < //. >; . int main() < Bar<>bar; // use all default type arguments >
Специализация шаблонов
В некоторых случаях невозможно или желательно определить точно тот же код для любого типа. Например, может потребоваться определить путь к коду, который необходимо выполнить, только если аргумент типа является указателем или std::wstring или типом, производным от определенного базового класса. В таких случаях можно определить специализацию шаблона для конкретного типа. Когда пользователь создает экземпляр шаблона с этим типом, компилятор использует специализацию для создания класса, а для всех других типов компилятор выбирает более общий шаблон. Специализации, в которых все параметры являются специализированными, являются полными специализациями. Если только некоторые из параметров специализированы, он называется частичной специализацией.
template class MyMap*. */>; // partial specialization for string keys template class MyMap *. */>; . MyMap classes; // uses original template MyMap classes2; // uses the partial specialization
Шаблон может иметь любое количество специализаций, если каждый параметр специализированного типа является уникальным. Только шаблоны классов могут быть частично специализированы. Все полные и частичные специализации шаблона должны быть объявлены в том же пространстве имен, что и исходный шаблон.
Дополнительные сведения см. в разделе «Специализация шаблона».
Обратная связь
Были ли сведения на этой странице полезными?
Работа с кодом С++ в Конструкторе классов
Конструктор классов отображает визуальную область конструктора, которая называется диаграммой классов и предоставляет визуальное преставление элементов кода в проекте. Схемы классов можно использовать для разработки и визуализации классов и других типов в проекте.
Конструктор классов поддерживает следующие элементы кода C++:
- Класс (похож на фигуру управляемого класса, за исключением того, что он может иметь несколько отношений наследования)
- Анонимный класс (отображает созданное представлением класса имя для анонимного типа)
- Класс шаблона
- Структура
- Перечисление
- Макрос (отображает представление макроса после обработки)
- Typedef
Это не аналог схемы классов UML, которую можно создать в проекте моделирования. Дополнительные сведения см. в разделе UML-схемы классов: справочник.
Классы проектирования C++ в конструкторе классов
Конструктор классов поддерживает классы C++ и визуализирует собственные классы C++ так же, как фигуры классов Visual Basic и C#. Отличие состоит в том, что классы C++ могут обладать отношениями множественного наследования. Фигуру класса можно развернуть, чтобы отобразить дополнительные поля и методы класса, а также свернуть в целях экономии места.
Конструктор классов не поддерживает объединения (это особый тип класса, в котором объем выделяемой памяти равен размеру самого большого элемента данных объединения).
Простое наследование
Если перетащить на схему сразу несколько классов, между которыми есть отношение наследования, они будут соединены стрелкой. Стрелка указывает в направлении базового класса. Например, если на схеме классов отображаются следующие классы, они будут соединены стрелкой, которая будет указывать от класса B на класс A:
class A <>; class B : A <>;
Также можно перетащить на схему только класс B, щелкнуть правой кнопкой мыши его фигуру, а затем выбрать команду Показать базовые классы. В этом случае будет показан базовый класс A.
Множественное наследование
Конструктор классов поддерживает наглядное представление отношений множественного наследования. Множественное наследование используется, если у производного класса есть атрибуты более чем одного базового класса. Ниже приведен пример множественного наследования:
class Bird <>; class Swimmer <>; class Penguin : public Bird, public Swimmer <>;
Если перетащить на схему сразу несколько классов, между которыми есть отношение множественного наследования, они будут соединены стрелкой. Стрелка указывает в направлении базовых классов.
Чтобы просмотреть классы, которые являются базовыми для выбранного, щелкните правой кнопкой мыши фигуру класса и выберите Показать базовые классы.
Команда Показать производные классы в коде C++ не поддерживается. Чтобы просмотреть производные классы, перейдите в представление классов, разверните узел типа, затем разверните вложенную папку Производные типы и перетащите нужные типы на диаграмму классов.
Дополнительные сведения о множественном наследовании классов см. в разделах Множественное наследование и Несколько базовых классов.
Абстрактные классы
Конструктор классов поддерживает абстрактные классы (также называются «абстрактными базовыми классами»). Для этих классов не создаются экземпляры, но от них могут наследоваться другие классы. Используя пример из раздела «Множественное наследование» этого документа, вы можете создавать экземпляры класса Bird в виде отдельных объектов, как показано ниже:
int main()
Тем не менее вы не можете создавать экземпляры класса Swimmer в виде отдельных объектов. При этом вы можете создавать на его основе производные классы, например Penguin , Whale и Fish . В этом случае можно объявить Swimmer как абстрактный базовый класс.
Чтобы объявить класс как абстрактный, используйте ключевое слово abstract . Элементы, помеченные как абстрактные или включенные в абстрактный класс, являются виртуальными и должны быть реализованы классами, производными от абстрактного класса.
class Swimmer abstract < virtual void swim(); void dive(); >;
Также можно объявить класс как абстрактный, включив по крайней мере одну чисто виртуальную функцию:
class Swimmer < virtual void swim() = 0; void dive(); >;
При выводе этих объявлений на схеме классов имена класса Swimmer и его чисто виртуальной функции swim отображаются курсивом на фигуре абстрактного класса. Кроме того, будет показана надпись Абстрактный класс. Обратите внимание, что фигуры типа абстрактного класса аналогичны обычным классам, но их границы показаны пунктирной линией.
В классе, производном от абстрактного базового класса, необходимо переопределить каждую чисто виртуальную функцию в базовом классе. В противном случае создать экземпляр производного класса нельзя. Например, если необходимо наследовать класс Fish от класса Swimmer , в классе Fish необходимо переопределить метод swim :
class Fish : public Swimmer < void swim(int speed); >; int main()
При отображении данного кода на диаграмме классов конструктор классов проводит линию наследования от класса Fish к классу Swimmer .
Анонимные классы
Конструктор классов поддерживает анонимные классы. Типы анонимных классов объявляются без идентификатора. Они не могут иметь конструктор или деструктор, не могут передаваться в качестве аргументов в функции и не возвращаются в качестве значений из функций. Анонимный класс можно использовать для замены имени класса именем определения типа, как показано в следующем примере:
typedef struct < unsigned x; unsigned y; >POINT;
Структуры также могут быть анонимными. Конструктор классов отображает анонимные классы и структуры таким же образом, как и соответствующие типы. Несмотря на то, что можно объявить и отобразить анонимные классы и структуры, конструктор классов не будет использовать указанное имя тега. В этом случае будет использоваться имя, созданное в представлении классов. Класс или структура отображаются в представлении классов и конструкторе классов в виде элемента с именем __unnamed.
Дополнительные сведения об анонимных классах см. в разделе Типы анонимных классов.
Классы шаблона
Конструктор классов поддерживает визуализацию классов шаблона. Поддерживаются вложенные объявления. В следующей таблице показаны некоторые типичные объявления.
В следующей таблице показаны некоторые примеры частичной специализации.
В следующей таблице показаны некоторые примеры наследования в частичной специализации.
(указывает на класс A)
В следующей таблице приведены некоторые примеры функций шаблона частичной специализации.
void func(T a, U b);
void func(T a, int b);
В следующей таблице показаны некоторые примеры наследования шаблонов.
(Класс B содержится в классе C в разделе Вложенные типы)
В следующей таблице показаны некоторые примеры связей между классами в канонической специализации.
Перечисления C++ в конструкторе классов
Конструктор классов поддерживает типы C++ enum и типы enum class с областью видимости. Ниже приведен пример:
enum CardSuit < Diamonds = 1, Hearts = 2, Clubs = 3, Spades = 4 >; // or. enum class CardSuit < Diamonds = 1, Hearts = 2, Clubs = 3, Spades = 4 >;
Фигура перечисления C++ в схеме классов выглядит и работает подобно фигуре структуры, за исключением того, что она называется Перечисление или Класс перечисления, имеет розовый, а не синий цвет, и располагает цветной границей на левом и верхнем полях. Фигуры перечисления и структуры имеют прямые углы.
Дополнительные сведения об использовании типа enum см. в статье Enumerations (Перечисления).
Определения типов C++ в конструкторе классов
Операторы typedef создают один или несколько уровней косвенного обращения между именем и его базовым типом. Конструктор классов поддерживает те определения типов C++, которые объявлены с ключевым словом typedef , например:
typedef class coord < void P(x,y); unsigned x; unsigned y; >COORD;
Затем можно использовать этот тип для объявления экземпляра:
Фигуры для классов и структур
В конструкторе классов определение типа C++ имеет форму типа, указанного в определении типа. Если источник объявляет typedef class , у фигуры будут скругленные углы и метка класса. У typedef struct фигура имеет квадратные углы и метку Структура.
Классы и структуры могут иметь вложенные определения типов, объявленные внутри них. В конструкторе классов фигуры классов и структур могут отображать вложенные объявления определений типов в виде вложенных фигур.
Фигуры определения типов поддерживают команды Показывать как ассоциацию и Показывать как ассоциацию наборов контекстного меню.
Пример определения типа класса
class B <>; typedef B MyB;
Пример определения типа структуры
typedef struct mystructtag < int i; double f; >mystruct;
Неименованные определения типов
Несмотря на то, что можно объявить определение типа без имени, конструктор классов не будет использовать указываемое имя тега. Конструктор классов будет использовать имя, созданное в представлении классов. Например, следующее объявление является допустимым, но оно отображается в представлении классов и конструкторе классов как объект с именем __unnamed:
typedef class coord < void P(x,y); unsigned x; unsigned y; >;
Конструктор классов не отображает определение типа, если его исходный тип является указателем на функцию.
Сведения об ограничениях для элементов кода C++
- При загрузке проекта на С++ конструктор классов работает в режиме только для чтения. Вы можете изменить схему классов, но не можете сохранить изменения из схемы классов обратно в исходный код.
- Конструктор классов поддерживает только собственную семантику C++. Для проектов C++, которые компилируются в управляемый код, конструктор классов будет визуализировать только те элементы кода, которые являются собственными типами. Таким образом, вы можете добавить диаграмму классов в проект, но конструктор классов не позволит визуализировать элементы, в которых свойству IsManaged задано значение true (то есть типы значений и ссылочные типы).
- Для проектов C++ конструктор классов считывает только определение типа. Например, предположим, что вы определяете тип в файле заголовков (H) и определяете его члены в файле реализации (CPP). При выполнении команды «Просмотр схемы класса» для файла реализации (CPP) конструктор класса не отображает ничего. Еще один пример, если вы выполняете команду «Просмотр схемы класса» для CPP-файла, который использует оператор #include для включения других файлов, но не содержит фактические определения классов, конструктор класса снова не отображает ничего.
- IDL-файлы, которые определяют COM-интерфейсы и библиотеки типов, не отображаются в схемах, пока не будут скомпилированы в машинный код C++.
- Конструктор классов не поддерживает глобальные функции и переменные.
- Конструктор классов не поддерживает объединения. Это особый тип класса, в котором объем выделяемой памяти равен размеру самого большого элемента данных объединения.
- int Конструктор классов char не отображает базовые типы данных, такие как и .
- Конструктор классов не отображает типы, которые определены за пределами текущего проекта, если только проект не содержит правильные ссылки на эти типы.
- Конструктор классов может отобразить вложенные типы, но не отношения между вложенным типом и другими типами.
- Конструктор классов не может отобразить типы, которые относятся к void или являются производными от типа void.
Устранение неполадок при разрешении типов и отображение проблем
Расположение исходных файлов
Конструктор классов не отслеживает расположение исходных файлов. Таким образом, при изменении структуры проекта или перемещении исходных файлов в проекте конструктор классов может потерять тип (особенно исходный тип определения типа, базовых классов или типов связи). Может возникнуть ошибка, например Конструктору классов не удалось отобразить этот тип. В этом случае перетащите измененный или перемещенный исходный код в схему классов и повторно отобразите ее.
Проблемы с обновлением и производительностью
Для проектов Visual C++ для отображения изменения в исходном файле на схеме классов может потребоваться от 30 до 60 секунд. Из-за такой задержки конструктор классов может выдать ошибку В выбранном блоке не найдены типы. После возникновения подобной ошибки нажмите кнопку Отмена в сообщении об ошибке и дождитесь отображения элемента кода в представлении классов. После этого конструктор классов должен быть в состоянии отобразить этот тип.
Если схема классов не обновляется для вывода внесенных в код изменений, может потребоваться закрыть схему и снова открыть ее.
Проблемы разрешения типа
Конструктор классов может оказаться не в состоянии разрешить типы по следующим причинам:
- Тип находится в проекте или сборке, на которые нет ссылок из проекта, содержащего эту схему классов. Чтобы исправить эту ошибку, добавьте ссылку на проект или сборку, содержащие данный тип. Дополнительные сведения см. в статье Управление ссылками в проекте.
- Тип находится в неправильной области действия, поэтому конструктор классов не может его найти. Убедитесь, что в коде присутствует оператор using , imports или #include . Убедитесь также, что этот тип (или связанный тип) не был перемещен из пространства имен, в котором он находился изначально.
- Тип не существует (или был закомментирован). Чтобы исправить эту ошибку, убедитесь, что тип не закомментирован и не удален.
- Тип находится в библиотеке, на которую ссылается директива #import. В качестве возможного обходного пути можно вручную добавить сформированный код (TLH-файл) в директиву #include в файле заголовка.
- Убедитесь, что конструктор классов поддерживает введенный вами тип. См . ограничения для элементов кода C++.
Скорее всего, проблема разрешения типов возникает, если код не найден для одной или нескольких фигур в схеме классов «». Это сообщение об ошибке не обязательно означает, что код содержит ошибку. Оно указывает лишь на то, что конструктору классов не удалось отобразить код. Попробуйте сделать следующее.
- Убедитесь, что тип существует. Убедитесь, что исходный код не был случайно закомментирован или удален.
- Попытайтесь разрешить тип. Тип может находиться в проекте или сборке, на которые нет ссылок из проекта, содержащего эту схему классов. Чтобы исправить эту ошибку, добавьте ссылку на проект или сборку, содержащие данный тип. Дополнительные сведения см. в статье Управление ссылками в проекте.
- Убедитесь, что тип находится в правильной области действия, чтобы конструктор классов мог его найти. Убедитесь, что в коде присутствует оператор using , imports или #include . Убедитесь также, что этот тип (или связанный тип) не был перемещен из пространства имен, в котором он находился изначально.
Дополнительные сведения об устранении неполадок см. в разделе об ошибках конструктора классов.
Связанный контент
What is the equivalent of a template class in C# that inherits from the specified type? [duplicate]
The class above inherits from the specified type. MyClass is a base type, and this is the reason I want this behaviour. I want to be able to instantiate this object with classes derived from MyClass. I understand you cannot use templates in C#, but is there any way to get the behaviour I want without having to write a new class for each different type of object? EDIT — I want to be able to instantiate an object that will test the behaviour of any class, as long as that class is derived from a specified type — ‘MyClass’.
1 1 1 silver badge
asked Aug 28, 2012 at 14:52
Dollarslice Dollarslice
10.1k 22 22 gold badges 60 60 silver badges 89 89 bronze badges
@delnan I want to do it in C#
Aug 28, 2012 at 14:56
@SirYakalot: Please describe what you want to achieve with this construct
Aug 28, 2012 at 14:57
This reminds me of the CRTP, but why would you want to do it in C#?
Aug 28, 2012 at 15:00
2 Answers 2
In C# you have the concept of generics. The following code is equivalent to your C++ code:
class MockComposite : T where T : MyClass
Unfortunatelly, it is invalid. You will get the following compile error:
Cannot derive from ‘T’ because it is a type parameter
In C# you would usually solve this problem with composition instead of inheritance.
Composition in this case means one of two things:
-
Your class still has the generic argument but doesn’t try to derive from it. Furthermore, it has the new constraint, meaning that the generic argument needs to have a default constructor:
class MockComposite where T : MyClass, new() < T _toTest; public MockComposite() < _toTest = new T(); >>
This means you can instantiate the mock class like this: var mock = new MockComposite
class MockComposite < MyClass _toTest; public MockComposite(MyClass toTest) < _toTest = toTest; >>
answered Aug 28, 2012 at 14:54
Daniel Hilgarth Daniel Hilgarth
173k 40 40 gold badges 340 340 silver badges 446 446 bronze badges
@exacerbatedexpert: Maybe you want to read the whole answer. The C++ code translated to C# generic syntax is the code I posted. BTW: 2 downvotes? Do you have multiple accounts?
Aug 28, 2012 at 15:15
This is simply not possible in C#. But this is a case of trying to use one language as another, different, language. In C++ this pattern has its uses. You can accomplish the same things in C#, but using different techniques.
Maybe this will help you:
class Myclass < >class ChildClass : MyClass < >class MockComposite where T : MyClass < public MockComposite(T objectToTest) < . >>
Here, you can pass a any class that derives from MyClass into CompositeTester<> . The biggest difference between this and your code is that here you need to pass in real existing non-generic classes that inherit MyClass . You are trying to inherit generically which is not possible.
In C++ this is basically like doing:
class Myclass < >; class ChildClass : MyClass < >; template class MockComposite < public: MockComposite(const T& objectToTest) < . >>;
C# generics might look a bit like C++ templates but it’s an entirely different idea. While C++ templates let you do be very flexible almost to the point of being like macros, generics remain rather tight, type-wise. You cannot do anything with a generic type that you have not said it can do explicitly, hence generic constraints like where T : MyClass . By specifying that, MockComposite.MockComposite can use T as a MyType .