#c# #c -cli
#c -cli
Вопрос:
Я где-то читал в MSDN, что эквивалентом ключевого слова C # «is» было бы dynamic_cast , но на самом деле это не эквивалентно: оно не работает с типами значений или с универсальными параметрами. Например, на C # я могу написать:
void MyGenericFunction<T>()
{
object x = ...
if (x is T)
...;
}
Если я попробую «эквивалентный» C / 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:
Это в MSDN:
Как: Реализовать ключевые слова is и as C # в C
В двух словах, вам нужно написать вспомогательную функцию примерно так:
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");
Комментарии:
1. Возможно, вы неправильно поняли мой вопрос. Я знаю эту статью в MSDN. Я упомянул это в своем вопросе. И я объяснил, почему у меня это не работает. dynamic_cast не эквивалентен C # «as». Это работает только для ссылочных типов.
2. Упс, следует более внимательно прочитать вопросы. Это работает для универсальных типов, но не для типов значений.
3. C #
as
также не работает для типов значений:dynamic_cast
является точным эквивалентомas
. Использоватьsafe_cast
для приведения к типам значений. Семантика эквивалентна семантике C #: генерирует исключение для неверных приведений к типам значений, возвращаетnull
для неверных приведений к ссылочным типам.
Ответ №2:
Вы можете использовать safe_cast
то, что вы использовали бы dynamic_cast
в родном C , и перехватить исключение System::InvalidCastException. С точки зрения совместимых типов, семантика запроса, можете ли вы конвертировать типы, может охватывать более широкий диапазон типов, чем проверка идентичности. Возможно, вам действительно нужна дополнительная гибкость IsAssignableFrom.
Я не думаю, что существует эффективный эквивалент старой доброй dynamic_cast
идиомы, к которой мы привыкли, и уж точно ничего более компактного.
Ответ №3:
Хотя простым обходным путем было бы использовать safe_cast<T>(x)
и перехватывать 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
оператора C #. Он использует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)
это последняя вспомогательная функция, которая выберет шаблон для использования.