C++/CLI问题:是否有与C#的"is"关键字等效的东西,还是我必须使用反射?

15

我在 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))
       ...;
}

我遇到了一个编译器错误 "error C2682: cannot use 'dynamic_cast' to convert from 'System::Object ^' to 'T'"。
我唯一能想到的方法是使用反射技术:
if (T::typeid->IsAssignableFrom(obj->GetType()))

有没有更简单的方法来做这件事?
3个回答

16

这篇文章在MSDN上可以找到:

如何在C++中实现is和as 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");

3
也许你误解了我的问题。我知道那篇MSDN文章,我在我的问题中提到了它。并且我解释了为什么它对我行不通。dynamic_cast并不等同于C#的“as”操作符。它只适用于引用类型。 - Niki
哎呀,应该更仔细地阅读问题。它适用于通用类型,但不适用于值类型。 - Guido Domenici
12
C#的as对于值类型也不起作用:dynamic_castas的确切等价物。将值类型强制转换使用safe_cast。语义与C#相同:对于向值类型的不良转换,抛出异常;对于引用类型的不良转换,返回null - Alexandre C.

7
你可以在本机C++中使用safe_cast代替dynamic_cast,并捕获System::InvalidCastException。在涉及到兼容类型的语义上,询问是否可以转换类型可能会涵盖更广泛的类型范围,而不是检查标识。你可能实际上想要IsAssignableFrom的额外灵活性。
我认为我们习惯于使用的好老的dynamic_cast习惯没有有效的等效物,当然也没有那么紧凑。

1

虽然一个简单的解决方法是使用 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);
}

一点解释:

是HTML标记,用于创建段落。

  • is_instance_of_managed_helper 是一个托管类,它在运行时生成一个函数,给出了 C# 的 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) 是最终的帮助函数,它将选择要使用的模板。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接