I\'ve read somewhere on MSDN that the equivalent to C#\'s \"is\" keyword would be dynamic_cast, but that\'s not really equivalent: It doesn\'t work with value types or with
While a simple workaround would be to use safe_cast<T>(x)
and catch System::InvalidCastException^
, this has the obvious overhead of exception handling (unrolling the stack and all the related fun) when the type doesn't match.
I tried to come up with a different approach. While I wouldn't exactly call it simple, it does its job without using exceptions.
#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);
}
A bit of explanation:
is_instance_of_managed_helper
is a managed class which generates a function at runtime giving the equivalent of C#'s is
operator. It uses Expression::TypeIs to achieve this in a simple way. One such function will be generated once for every T
.
template <typename T> struct is_instance_of_helper
is a template struct which simply uses the above solution. This is the general case.
template <typename T> struct is_instance_of_helper<T^>
is a partial specialization of the above struct which uses dynamic_cast
for managed handle types. This way, we'll spare ourselves the trouble of generating code at runtime when T
can simply be used with dynamic_cast
.
template <typename T> bool is_instance_of(System::Object^ obj)
is the final helper function which will choose the template to use.
You can use safe_cast
where you would use dynamic_cast
in native C++ and trap the System::InvalidCastException. In terms of compatible types the semantics of asking if you can convert types could pick up a broader range of types than checking identity. You may actually want the added flexibility of IsAssignableFrom.
I don't think there's an efficient equivalent to the good old dynamic_cast
idiom we're used to, certainly nothing as compact.
It's on MSDN:
How to: Implement is and as C# Keywords in C++
In a nutshell, you need to write a helper function like so:
template < class T, class U >
Boolean isinst(U u) {
return dynamic_cast< T >(u) != nullptr;
}
and call it like this:
Object ^ o = "f";
if ( isinst< String ^ >(o) )
Console::WriteLine("o is a string");