简体   繁体   中英

Determine at runtime if a cast is allowed (C#)

I have a C# wraper class with a series of methods accepting various data types:

public class MyClass
{
    public void ProcessString(string Value) { // implementation }
    public void ProcessInt(int? Value) { // implementation }\
    public void ProcessOther(MyClass Value) { // implementation }
}

I now want to add a generic ProcessObject() method to avoid the need to explicitly cast an object before calling a relevant process method:

public void ProcessObject(object Value)
{
    if (CanCastToString(Value)
    {
        ProcessString((string)Value);
    }
    else if (CanCastToInt(Value))
    {
        ProcessInt((int?)Value);
    }
    // etc...
}

The trouble is that I don't know what my CanCastToInt methods should be - I need these methods to be able to be robust and deal with things like nullable types and other user defined casts.

How can I do this? All I want to know is if a given object can be cast to a given type, ie whether or not:

(SomeType)Value

Will work.

There are two main ways this is usually done:

if (Value is SomeType)
{
    // Do something with a cast
}

or

var v = Value as SomeType;
if (v != null)
{
    // Value was successfully cast as SomeType
}

When working with structs or intrinsic types, make them nullable:

var v = Value as int?;
if (v != null)
{
    ProcessInt(v.Value);
}

you need is operator. CanCastToString(x) -> x is string .

Why not expose your processing API directly, with overloads for various parameters?

public class MyClass
{
    public void Process(string Value) { // implementation }
    public void Process(int Value) { // implementation }\
    public void Process(MyClass Value) { // implementation }
    public void Process(object Value) { // catch all method. Handle unknown entities, e.g. call ToString() }
}

EDIT With some generics magic you can have a single interface method, a bunch of helper methods that do the work and one catch-all method where you handle corner cases.

public class MyClass
{
    void DoProcess(string Value) { // implementation }
    void DoProcess(int Value) { // implementation }\
    void DoProcess(MyClass Value) { // implementation }
    void DoProcess(object Value) { 
        // catch all method. Handle unknown entities, e.g. call ToString()
    }
    public void Process<T>(T value) {
       //this method will call the right overload of DoProcess depending on the compile time type of value. If there isn't a match, it goes to DoProcess(object)
       DoProcess(value);
    }
}

This way you avoid boxing for fundamental types and have slightly better type safety.

For your catch-all method you can try using Type.IsAssignableFrom method. For example:

if (typeof(short).IsAssignableFrom(Value)
    DoProcess((short)Value);
if (typeof(byte).IsAssignableFrom(Value)
    DoProcess((byte)Value);

I do recommend that you read Eric Lippert's essay about representation casts. Hopefully, after doing that, you will come to realisation that it might just be easier to have an overload for each supported type. Also, you might realise that dealing with unboxing value types could be a road to hell.

If (unlike OP?) you don't know the type involved until runtime you may try to employ some variation of this:

http://codegoeshere.blogspot.com/2007/05/dynamic-cast-in-c.html

    public void QuickTest()
    {
        object stringObj = "string";
        object nullableInt1 = (int?)null;
        object nullableInt2 = (int?)1;
        object decimalObj = 1.5m;

        ProcessObject(stringObj);
        ProcessObject(nullableInt1);
        ProcessObject(nullableInt2);
        ProcessObject(decimalObj);
    }

    public void ProcessObject(object value)
    {
        if (value == null)
        {
            Debug.WriteLine("null");
            return;
        }


        if (value is string)
        {
            Debug.WriteLine((string)value);
            return;
        }

        string stringValue = value.ToString();

        int intTemp;
        if (int.TryParse(stringValue, out intTemp))
        {
            Debug.WriteLine(intTemp);
            return;
        }

        decimal decimalTemp;
        if (decimal.TryParse(stringValue, out decimalTemp))
        {
            Debug.WriteLine(decimalTemp);
            return;
        }
        // etc...
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM