Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I override ToString() for classes in a public API?

I'm writing a tool that interfaces with an API for another piece of software. Part of my tool will need to generate reports about the various objects found through the API, and I want these reports to contain simple strings that identify each object. By default I plan to use ToString() to generate the string for each object. However, not surprisingly I've found that the default ToString() implementations in this API aren't to descriptive.

Initially I was thinking of doing something like the code below with a long Switch statement. Although this would most likely become unmanageably long.

public string GetAPIObjectDescrition(object obj)
{
     Type t = obj.GetType();

     Switch(t)
     { 
         Case typeof(SomeAPIType):
             SomeAPIType x = (SomeAPIType)obj;
             return  x.SomeProperty;             


         Case typeof(SomeOtherAPIType):
             SomeOtherAPITypex = (SomeOtherAPIType)obj;
             return  x.SomeOtherProperty;

         default:
             return x.ToString();
     }
} 

Next I tried using extension methods (see the code below). CustomObjectDescription() worked as expected, but when I tried to call ToString() it just returns the default ToString() results. I've never used extension methods before so I could be completely off base thinking something like this is even possible.

I can't guarantee that there will be a CustomObjectDescription() extension for every Type encountered in the API, so if I take this route I would end up having to use reflection each time to check if the current object has a GetObjectDescription() extension. I'd like to avoid using reflection if at all possible.

public static class APIObjectDescriptionExtensions
{
    public static string ToString(this APIObject element)
    {
        return "ElementName = " + element.Name + " ElementID =" + element.Id.IntegerValue.ToString();
    }

    public static string CustomObjectDescription(this APIObject element)
    {
        return "ElementName = " + element.Name + " ElementID =" + element.Id.IntegerValue.ToString();
    }
}

Does anyone have any other suggestions on how I should approach this problem? I'd prefer a solution where the code for each API Type is independent from one another (no giant Switch statement).

Also if possible I'd like the description string code for one type to inherit to sub types unless those types have their own unique description string code.

I think there might be a better solution that involves creating custom TypeConverters or maybe overriding/extending System.Convert.ToString()?


Update

I think the example below might help clarify what I'm trying to do. Ultimately I want to be able to take any arbitrary class from this API, the Type of which is not known until run time, and generate a description string. If the Type has my custom extension method then it should be used, otherwise the code should fall back on plain old ToString().

    public static string GetDataDescription(object o)
    {
        //get the type of the input object
        Type objectType = o.GetType();

        //check to see if a description extension method is defined
        System.Reflection.MethodInfo extensionMethod = objectType.GetMethod("MyDescriptionExtensionMethod");

        if (extensionMethod != null)
        {
            //if a description extension method was found returt the result
            return (string)extensionMethod.Invoke(o, new object[] { });
        }
        else
        {
            //otherwise just use ToString();
            return o.ToString();
        }
    }

This code above doesn't work though because extension methods aren't found by GetMethod().

like image 735
Eric Anastas Avatar asked Nov 05 '22 06:11

Eric Anastas


1 Answers

You could provide a wrapper for each of the classes similar to this:

    public class SomeAPITypeWrapper : SomeAPIType
    {
        public override string ToString()
        {
            return SomeProperty;
        }
    }

    public class SomeOtherAPITypeWrapper : SomeOtherAPIType
    {
        public override string ToString()
        {
            return SomeOtherProperty;
        }
    }

This certainly allows for using base classes/sub classes as requested in your question. It also keeps it clean and within your object model itself instead of in a switch statement or helper class.

like image 55
Chris Conway Avatar answered Nov 09 '22 06:11

Chris Conway