С++/CLI-Question: Существует ли эквивалент ключевому слову С# "is" или мне нужно использовать отражение?
Я где-то читал в MSDN, что эквивалент ключевого слова С# будет "dynamic_cast", но это не совсем эквивалентно: он не работает со значениями типов или с общими параметрами. Например, в С# я могу написать:
void MyGenericFunction<T>()
{
object x = ...
if (x is T)
...;
}
Если я попробую "эквивалентный" С++/CLI:
generic<class T>
void MyGenericFunction()
{
object x = ...
if (dynamic_cast<T>(x))
...;
}
Я получаю ошибку компилятора "ошибка C2682: не может использовать 'dynamic_cast' для преобразования из 'System:: Object ^' в 'T'.
Единственное, о чем я могу думать, это использовать отражение:
if (T::typeid->IsAssignableFrom(obj->GetType()))
Есть ли более простой способ сделать это?
Ответы
Ответ 1
Вы можете использовать safe_cast
, где вы бы использовали dynamic_cast
в родном С++ и ловушку System:: InvalidCastException. Что касается совместимых типов, то семантика запроса, если вы можете конвертировать типы, может выбрать более широкий диапазон типов, чем проверка личности. На самом деле вам может понадобиться дополнительная гибкость IsAssignableFrom.
Я не думаю, что существует эффективный эквивалент старой доброй dynamic_cast
идиомы, к которой мы привыкли, конечно, ничего не компактного.
Ответ 2
Это на MSDN:
Практическое руководство. Реализация - это и как ключевые слова С# в С++
В двух словах вам нужно написать вспомогательную функцию:
template < class T, class U >
Boolean isinst(U u) {
return dynamic_cast< T >(u) != nullptr;
}
и назовите его следующим образом:
Object ^ o = "f";
if ( isinst< String ^ >(o) )
Console::WriteLine("o is a string");
Ответ 3
В то время как простым обходным путем было бы использовать safe_cast<T>(x)
и catch System::InvalidCastException^
, у этого есть очевидные накладные расходы на обработку исключений (разворачивание стека и все связанное с этим удовольствие), когда тип не соответствует.
Я попытался придумать другой подход. Хотя я бы не назвал его простым, он выполняет свою работу без использования исключений.
#using <System.Core.dll>
namespace detail
{
generic <typename T> ref class is_instance_of_managed_helper sealed abstract
{
public:
static initonly System::Func<System::Object^, bool>^ is_instance_of = build();
private:
static System::Func<System::Object^, bool>^ build()
{
using System::Linq::Expressions::Expression;
auto param = Expression::Parameter(System::Object::typeid);
return Expression::Lambda<System::Func<System::Object^, bool>^>(
Expression::TypeIs(param, T::typeid),
param)->Compile();
}
};
template <typename T> struct is_instance_of_helper
{
static bool is_instance_of(System::Object^ obj)
{
return is_instance_of_managed_helper<T>::is_instance_of(obj);
}
};
template <typename T> struct is_instance_of_helper<T^>
{
static bool is_instance_of(System::Object^ obj)
{
return dynamic_cast<T^>(obj) != nullptr;
}
};
}
template <typename T> bool is_instance_of(System::Object^ obj)
{
return detail::is_instance_of_helper<T>::is_instance_of(obj);
}
Немного объяснения:
-
is_instance_of_managed_helper
- управляемый класс, который генерирует функцию во время выполнения, предоставляя эквивалент оператора С# is
. Он использует Expression::TypeIs
, чтобы достичь этого простым способом. Одна такая функция будет генерироваться один раз для каждого T
.
-
template <typename T> struct is_instance_of_helper
- это шаблонная структура, которая просто использует указанное выше решение. Это общий случай.
-
template <typename T> struct is_instance_of_helper<T^>
является частичной специализацией вышеприведенной структуры, которая использует dynamic_cast
для управляемых типов дескрипторов. Таким образом, мы избавим себя от необходимости генерировать код во время выполнения, когда T
можно просто использовать с dynamic_cast
.
-
template <typename T> bool is_instance_of(System::Object^ obj)
- это последняя вспомогательная функция, которая выберет используемый шаблон.