Подтвердить что ты не робот

С++/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()))

Есть ли более простой способ сделать это?

4b9b3361

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