开发者

C#: How to perform 'as' operation with a Type

开发者 https://www.devze.com 2023-04-07 01:04 出处:网络
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:

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

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号