C#: How to perform 'as' operation with a Type
I want to test whether a given object
can be cast to a given Type
.
In this scenario, I have an object, and the Type
representing what type I want to cast it to:
public function FooBar(..., object data, Type expected) {
...
var unboxedData = ?
if (unboxedData == null) {
....
}
...
}
How can I cast data
to t开发者_Go百科he type type
represents?
Basically, I want to do this:
var unboxedData = data as Type;
...but of course you can't use Type
with an as
statement, so what do I do?
Edit 2: I'm going to say it's not possible without reflection or generics. With reflection, you have no compile-time checking and must use reflection (or dynamic
) to further call methods/properties of the object. With generics, you can't use a Type object to get there alone. Take your pick. Is it possible to refactor your calling code to allow generics?
If allowed, this may be more easily handled with a generic method:
public resultType FooBar<T>(..., object data) {
...
T unboxedData = (T)data;
...
}
Edit: Also, you can use data as T
if you include a generic type constraint of where T : class
:
public something FooBar<T>(..., object data)
where T : class
{
...
T unboxedData = data as T;
if (unboxedData == null) {
...
}
...
}
...but of course you can't use Type with an as statement, so what do I do?
Morre importantly, you can't use var
this way. So there is nothing to be gained here.
You can test if it's the right type with
if (expected.IsInstanceOfType(data))
But then you still can't write any decent code to access properties or methods on data
.
C# provides the as keyword to quickly determine at runtime whether a given type is compatible with another. When you use the as keyword, you are able to determine compatibility by checking against a null return value. Consider the following:
Hexagon hex2 = frank as Hexagon;
if (hex2 == null)
Console.WriteLine("Sorry, frank is not a Hexagon...");
In addition to the as keyword, the C# language provides the is keyword to determine whether two items are compatible. Unlike the as keyword, however, the is keyword returns false, rather than a null reference, if the types are incompatible.
if (emp is SalesPerson)
{
Console.WriteLine("{0} made {1} sale(s)!", emp.Name,
((SalesPerson)emp).SalesNumber);
}
if (data.GetType() == t || data.GetType().IsSubclassOf(t))
{
//do your thing
}
Should tell you if it's exactly or a subclass of (so it can be cast in to it).
This is pretty tricky. The problem is that var
does not mean "variant". It is acts more like a temporary placeholder that C# fills in with an actual type once the type information can be inferred from the expression. unboxedData
is still very much a strongly typed variable. Its just the compiler is trying to figure out the type instead of you explicitly specifying it. It is is of vital importance to note that the typing is still occurring at compile time and not runtime.
If you want to dynamically cast an object at runtime then you will not be able to use var
or any other concrete type specifier.
Your options are limited to one of the two possible declarations:
- object
- dynamic
Based on what I think you want to do with unboxedData
I suspect dynamic
is the route you want to go because it would allow you to call any method on the target Type
.
So here is what I came up with.
public void FooBar(object value, Type expected)
{
dynamic unboxedData = expected.FromObject(value);
unboxedData.CallSomeMethodDefinedInTheTargetType(); // This will work.
}
This requires the following extension method.
public static class TypeExtension
{
public static object FromObject(this Type target, object value)
{
var convertable = value as IConvertible;
if (convertable != null)
{
return convertable.ToType(target, null);
}
Type type = value.GetType();
if (target.IsAssignableFrom(type))
{
return value;
}
MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public);
foreach (MethodInfo mi in methods)
{
if (mi.ReturnType == target)
{
try
{
return mi.Invoke(null, new object[] { value });
}
catch (TargetInvocationException caught)
{
if (caught.InnerException != null)
{
throw caught.InnerException;
}
throw;
}
}
}
throw new InvalidCastException();
}
}
The cast will work if one of the following are true.
- The value to be converted implements
IConvertible
and has a conversion path to the target type. - The value to be converted subclasses the target type.
- The value to be converted defines an explicit conversion operator in its class declaration.
Well, looking around I found somthing... How to check if implicit or explicit cast exists?
Be wary, I haven't given it much testing, but at a glance it seems to be promising. A big negative is that it throws the exception if it can't convert it:
static bool isConvertableTo(object o, Type t)
{
try
{
var expr = Expression.Constant(o);
var res = Expression.Convert(expr, t);
return true;
}
catch { }
return false;
}
Another useful link with same approach: Checking if a type supports an implicit or explicit type conversion to another type with .NET
精彩评论