【问题标题】:C++/CLI-Question: Is there an equivalent to the C# "is" keyword or do I have to use reflection?C++/CLI-问题:是否有与 C#“is”关键字等效的关键字,还是必须使用反射?
【发布时间】:2009-04-03 07:17:35
【问题描述】:

我在 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()))

有没有更简单的方法来做到这一点?

【问题讨论】:

    标签: c++-cli


    【解决方案1】:

    在 MSDN 上:

    How to: Implement is and as C# Keywords in 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");
    

    【讨论】:

    • 也许你误解了我的问题。我知道那篇 MSDN 文章。我在我的问题中提到了它。我解释了为什么它对我不起作用。 dynamic_cast 不等同于 C#“as”。它仅适用于引用类型。
    • 糟糕,应该更仔细地阅读问题。它适用于泛型类型,但不适用于值类型。
    • C# 的 as 也不适用于值类型:dynamic_cast 完全等同于 as。使用safe_cast 强制转换为值类型。语义等同于 C#:将错误强制转换为值类型时抛出异常,将错误强制转换为引用类型返回 null
    【解决方案2】:

    您可以在本机 C++ 中使用 dynamic_cast 的地方使用 safe_cast 并捕获 System::InvalidCastException。就兼容类型而言,询问是否可以转换类型的语义可以选择比检查身份更广泛的类型。您实际上可能想要 IsAssignableFrom 增加的灵活性。

    我不认为有一个有效的等价于我们习惯的古老的 dynamic_cast 成语,当然没有什么比它更紧凑了。

    【讨论】:

      【解决方案3】:

      虽然一个简单的解决方法是使用 safe_cast&lt;T&gt;(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 是一个托管类,它在运行时生成一个函数,相当于 C# 的 is 运算符。它使用Expression::TypeIs 以简单的方式实现此目的。将为每个T 生成一个这样的函数一次

      • template &lt;typename T&gt; struct is_instance_of_helper 是一个简单使用上述解决方案的模板结构。这是一般情况。

      • template &lt;typename T&gt; struct is_instance_of_helper&lt;T^&gt; 是上述结构的部分特化,它将dynamic_cast 用于托管句柄类型。这样,当T 可以简单地与dynamic_cast 一起使用时,我们就可以省去在运行时生成代码的麻烦。

      • template &lt;typename T&gt; bool is_instance_of(System::Object^ obj) 是选择要使用的模板的最终辅助函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-01
        • 1970-01-01
        • 2010-12-31
        • 2011-03-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多