C / CLI-Вопрос: Есть ли эквивалент ключевому слову C # «is» или я должен использовать отражение?

#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) это последняя вспомогательная функция, которая выберет шаблон для использования.