5

I am writing an interop between a php service and our crm. One of the things I need to do is make sure that simple types get converted ToString() for use later in a json converter.

I am not sure even what the name is for 'simple types' but it can be defined like this... "an object that represents a low level variable type, containing a single value, not a class or anything with executable functions etc"

I've found that int, string, bool, double, and surprisingly enum will ToString() with pretty predictable results.

int x = 0;
bool y = true;
double z = 1.59 // money
CustomEnum theEnum = CustomEnum.somevalue;

x.ToString() results in "0"

y.ToString() results in "true"

z.ToString() results in "1.59"

theEnum.ToString() results in "somevalue"

But if I use this:

List<int> iList = new List<int>();
iList.Add(1);

MyClass theClass = new MyClass();

iList.ToString() results in "System.Collections.Generic.List`1[System.Int32]" theClass.ToString() results in "STTI.NKI.Interop.MyClass"

I'm not limited to lists. I could have an ExpandoObject, or a class etc.

I understand EXACTLY why this happens, and I want to know if there is a quick way to determine if an object of unknown type will ToString() into an expected value, and not the type name. I find it an antipattern to do something like

switch (theObject.GetType())
 case typeof(int):
 case typeof(bool):
 case typeof(doulble):
etc

I am not sure what the terms are, so googling my answer is proving difficult.

CarComp
  • 1,929
  • 1
  • 21
  • 47
  • If list, use String.Join(",",myList) to get a CSV string of all values – Shannon Holsinger Sep 16 '16 at 17:32
  • The result is always a string, so how can you have a value? (except with `string.ToString`) – leppie Sep 16 '16 at 17:34
  • 3
    well If you're in control of the types that your service/Interface will expose, you could just make sure that every exposed object overrides ToString() in a way that it renders a readable representation of the contents. @leppie if I got it right, OP wants to know if it is possible to inspect a type for whether or not its ToString() override (or base class default) will render a gist of the object's content, or a representation of its type/generic – Cee McSharpface Sep 16 '16 at 17:37
  • You got it right. My terminology is failing me at explaining this. Think about this... Imagine an integer. 0. Now, imagine it as a string. Easy. "0". Now imagine a class as a string. It doesn't make sense. Classname doesn't really cut it IMO. Its like ToString() is doing 2 different things. Creating a string representation of the value, or outputting the type name. How can I figure this out? – CarComp Sep 16 '16 at 17:37
  • If the type is a container, you'll get the name of the type. If not, you'll get the string of the value. That's why you use a method like String.Join() to get string representations of containers in a manner similar to int.ToString() – Shannon Holsinger Sep 16 '16 at 17:39
  • In this manner (using layman's terms), an int will go to "3", a double will go to "3.33," and a list or an array will go to "3,3,1,2,3" – Shannon Holsinger Sep 16 '16 at 17:40
  • Googled "how to determine if a type is a container". Not finding what I expected to find. – CarComp Sep 16 '16 at 17:40
  • In your question, you mentioned that you were executing a SWITCH and you get "iList.ToString() results in "System.Collections.Generic.List`1[System.Int32]" so you don't need to Google it 'cause you already have it. For classes, you could do KVP for each property and value (or field and value) depending on what's in the class – Shannon Holsinger Sep 16 '16 at 17:44

7 Answers7

6

So you want to check whether a type has a overridden ToString method? Why not just check whether the value returned by ToString is equal to the value returned by the default implementation of ToString?

From here, we know the default implementation of ToString is

return GetType().ToString();

So, we can use this to check whether an object has overridden the ToString method:

bool toStringOverridden = someObject.GetType().ToString() !=
    someObject.ToString();
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • This is an excellent trick. I can use this to make my comparison. For now, I'm going to either return myobject.ToString() if the ToString != Type name, or throw an error if they are equal. It also is technically the correct answer since i asked for a way to do it. Your true / false solution matches my question the best, although there are MANY good answers here, and lots of great information. – CarComp Sep 16 '16 at 18:09
  • Are you sure not using string.Equals to compare strings? – shtse8 Jul 29 '18 at 12:50
  • 1
    @shtse8 this is c#, not Java. – Sweeper Jul 29 '18 at 12:51
3

The ToString method is a virtual one and the default implementation is defined in the Object class and simply returns the name of the type of the object:

public virtual string ToString()
{
  return this.GetType().ToString();
}

int for example, overrides this method to return a meaningful representation.

What you can do is use reflection to detect whether a type overrides the ToString method like this:

public static bool OverridesToString(Type type)
{
    return type.GetMethod("ToString", new Type[0]).DeclaringType != typeof(object);
}

If it does, there is a very good chance that the ToString method would return something meaningful.

Yacoub Massad
  • 27,509
  • 2
  • 36
  • 62
  • 1
    Wouldn't it be more useful to compare `obj.ToString()` with `obj.GetType().ToString()`? Even if an object overrides `ToString`, it still can return the type name. – Sweeper Sep 16 '16 at 17:53
  • @Sweeper, Why would someone override `ToString` just to return the name of the type? These approaches are all heuristics. What if someone returns a random string? Or the non-full type name? – Yacoub Massad Sep 16 '16 at 18:02
0

Option 1: make sure that every Object will overwrite ToString(). Option 2: Use reflection to get all object properties and concat them.

Avi
  • 1,924
  • 3
  • 17
  • 31
0

Maybe you can do something similar to this:

bool ToStringIsTyped<T>(T myObj)
{
    return myObj.ToString().Contains(typeof(T).FullName);
}

It may not work in all cases, but possibly could be expanded

Felipe
  • 10,606
  • 5
  • 40
  • 57
0

I Think this is what you are looking, in the GetMethod the second argument is an empty array to watch for the .ToString(), just convert the i.GetType().GetMethod("ToString", new Type[] { }).DeclaringType == typeof(object) to a function and there you go.

class Program
    {
        static void Main(string[] args)
        {
            int i = 55;
            var s = "some string";
            var x = new List<string>();
            Console.WriteLine(i.ToString());
            Console.WriteLine(i.GetType().GetMethod("ToString", new Type[] { }).DeclaringType == typeof(object));
            Console.WriteLine(s.ToString());
            Console.WriteLine(s.GetType().GetMethod("ToString",new Type[]{}).DeclaringType == typeof(object));
            Console.WriteLine(x.ToString());
            Console.WriteLine(x.GetType().GetMethod("ToString",new Type[]{}).DeclaringType == typeof(object));
        }
    }
Jeradotz
  • 66
  • 7
0

...way to determine if an object of unknown type will ToString() into an expected value, and not the type name...

The default implementation of ToString() on object, according to documentation, returns "the fully qualified name of the object's type".

So we could come up with the hypothesis that whenever ToString() is overridden, its output will be "useful" in the sense you specified in the question.

To detect whether a function called is an override, we can make use of this answer, like so:

if(typeof(ObjectX).GetMethod("ToString").DeclaringType == typeof(ObjectX))
{
    /* ObjectX has overridden ToString() */
}
else
{
    /* ObjectX has inherited ToString() from its base class(es) */
}
Community
  • 1
  • 1
Cee McSharpface
  • 8,493
  • 3
  • 36
  • 77
  • Getting a lot of AmbiguousMatchExceptions using this code. I think it needs the new Type[0] part to work properly. Also the parenthesis are not quite right. – CarComp Sep 16 '16 at 18:02
0

Using reflection can add too much overhead, so I reckon it's better to create a generic method and add a constraint like: where T : IFormattable

MRP
  • 499
  • 5
  • 24