65

I have the C# method

private static string TypeNameLower(object o)
{
   return o.GetType().Name.ToLower();
}

to give me the lower case type name of the input object.

But if input is a string set to null or a nullable int set to null then this method of course fails.

How do I get the type name in this situation?

  • 5
    `return o.?.GetType().Name ?? "null"` and the duplicate is a different question imho – CAD bloke Jan 02 '17 at 22:59
  • Agreed, not a duplicate. The root question is duplicated, but the specifics are not. I wish there a way to challange some of these "marked as" flags. Its irresponsible for people to mark things like this by title alone, rather than fixing the title. –  Jul 30 '19 at 15:46

11 Answers11

68

Jeff is correct. That's like asking what kind of cake would have been in an empty box with no label.

As an alternative to Fortran's answer you could also do:

string TypeNameLower<T>(T obj) {
   return typeof(T).Name.ToLower(CultureInfo.InvariantCulture);
}

string TypeNameLower(object obj) {
   if (obj != null) { return obj.GetType().Name.ToLower(CultureInfo.InvariantCulture); }
   else { return null; }
}

string s = null;
TypeNameLower(s); // goes to the generic version

That way, C# will pick the generic one at compile time if it knows enough about the type you're passing in.

jrh
  • 405
  • 2
  • 10
  • 29
Josh
  • 68,005
  • 14
  • 144
  • 156
  • 4
    I suppose the answer I was looking for is that even though i have told the compiler what type i intend to fill into a variable, then if that variable is set to null i cannot retrieve the intended type. –  May 30 '09 at 18:06
  • 5
    Beware that this approach gives the _compile-time type_ (also known as the _declared_ type) of the expression passed. That is often different from the run-time type (the actual type of the object instance when the program runs). So in many cases where the method of the question would not throw, the two methods don't agree. As an example, consider the variable `ICloneable c = "hello";` The method of the question would return `typeof(string)`, while the method of this answer, invoked as `TypeNameLower(c)` returns `typeof(ICloneable)`. – Jeppe Stig Nielsen Sep 02 '13 at 19:05
  • 10
    `That's like asking what kind of cake would have been in an empty box with no label.`. Wait I know the answer to this. It was on a test once. Wouldn't the box contain both a cake and not a cake at the same time as long as you don't open the box? – Reactgular Jan 31 '14 at 17:19
  • 1
    @JeppeStigNielsen (and future readers) ricovox has a nice [answer below](http://stackoverflow.com/a/13874286/28411) to find the runtime type of non-null objects – RJFalconer Feb 18 '14 at 12:13
38

I thought I'd post my answer, even though this question is old, because in my opinion, the accepted answer is wrong. That answer was pretty creative, so I don't mean to knock it. And for all I know, it could be what the OP really wanted.

But, as you'll see from my examples below, I think that in almost all cases, the idea of using the generic function described in the accepted answer is either (A) unnecessary or (B) flat-out wrong. I've copied the generic function I'm talking about from the accepted answer and pasted it below for reference:

string TypeNameLower<T>(T obj) {
    return typeof(T).Name.ToLower();
}

Now, let's see some ways this function might be used:

Examples where the Generic Function is Unnecessary:

var s = "hello";
var t = TypeNameLower(s);

//or
foreach(char c in "banana")
    WriteLine(TypeNameLower(c));

//or
foreach(MyCustomStruct x in listOfMyCustomStruct)
    WriteLine(TypeNameLower(x));

In these examples the function works--that is, it does return the correct values which are "string", "char", and "mycustomstruct", respectively. However in all cases like these, (i.e. where the generic function actually does return the correct type), the compiler knows ahead of time what the defined type of the variable is, and so does the programmer, of course (unless they got confused about their variable names). So the function is completely unnecessary, and the programmer may as well have done this:

var s = "hello";
var t = "string";

//or
foreach(char c in "banana")
    WriteLine("char");

//or
foreach(MyCustomStruct x in listOfMyCustomStruct)
    WriteLine("mycustomstruct");

That might seem naive at first, but think about it for a while...it might take a while for it to really sink in...Try to come up with ANY scenario where using the generic function provides accurate information at Runtime that isn't already known (and hence could be auto-generated by the compiler or code-generation utilities such as T4 templates) at compile-time.

Now the point of the previous set of examples was just to demonstrate a minor annoyance with the generic function--that it is unnecessary in every case where it returns the correct result. But more importantly, take a look at the examples below. They demonstrates that in any other case, the result of the generic function is actually wrong if you expect the function to return the name of the true runtime type of the object. The function is actually only guaranteed to return the name of a type that the true value is assignable to, which might be an ancestor class, an interface, or "object" itself.

Examples where the Generic Function is Wrong

Stream ms = new MemoryStream();
IEnumerable str = "Hello";
IComparable i = 23;
object j = 1;

TypeNameLower(ms); //returns "stream" instead of "memorystream"
TypeNameLower(str); //returns "ienumerable" instead of "string"
TypeNameLower(i); //returns "icomparable" instead of "int32"
TypeNameLower(j); //returns "object" instead of "int32"
TypeNameLower<object>(true); //returns "object" instead of "bool"

In all cases, the results are quite wrong as you can see. Now, I admit that the last two lines were a bit contrived to demonstrate the point (not to mention that TypeNameLower(j) would actually be compiled to use the non-generic version of the function that is also part of the accepted answer--but you get the idea...)

The problem is that the function actually ignores the type of the object being passed in, and only uses the (compile-time) information of the generic parameter type to return the value.

A better implementation would be as follows:

string TypeNameLower<T>(T obj) {
    Type t;
    if (obj == null)
        t = typeof(T);
    else 
        t = obj.GetType();
    return t.Name.ToLower();
}

Now the function returns the name of the true runtime type whenever the object is non-null, and it defaults to the compile-time/defined type when the type is null.

Importantly, this function could be used WITHOUT a non-generic version!! The result would be that the function would never return null. The most general return value would be "object" e.g:

 object x = null; 
 string s = null;
 byte[] b = null;
 MyClass m = null;
 TypeNameLower(x); // returns "object"
 TypeNameLower(s); // returns "string"
 TypeNameLower(b); // returns "byte[]"
 TypeNameLower(m); // returns "myclass"

Note that this is actually more consistent with the defined objective of the function, as requested by the OP. That is, if the OP really does want to find out what the type-name of the object was if it weren't null, then returning null would NEVER be an appropriate answer, because null ISN'T the name of any Type, and typeof(null) isn't defined.

Every variable in C# descends from System.Object, so by definition, if the value weren't null then it would be an Object and that is in many cases the most that can be determined about a null reference at runtime.

drwatsoncode
  • 4,721
  • 1
  • 31
  • 45
  • 2
    So I agree with almost everything you wrote. However, for _"Try to come up with ANY scenario where using the generic function provides accurate information at Runtime that isn't already known (and hence could be auto-generated by the compiler or code-generation utilities such as T4 templates) at compile-time."_ If you're trying to use TypeNameLower from inside another generic function then the type is essentially known by the compiler but not by the programmer, so your hard-string solution like `foreach(char c in "banana") WriteLine("char");` doesn't seem reasonable. – Kevin Holt Nov 02 '16 at 22:24
  • Good points, with a language as high level as C# it's very easy to forget that generics are well, very similar to how they are in C++, in that they are mostly just a trick to make the compiler generate several different functions at compile time. I think answers like this are very important because C# may give the wrong impression that generics are dynamic, and fit in with the *runtime* type system -- they don't, they're compile time only. – jrh May 25 '18 at 13:34
19
// Uses the compiler's type inference mechanisms for generics to find out the type
// 'self' was declared with in the current scope.
static public Type GetDeclaredType<TSelf>(TSelf self)
{
    return typeof(TSelf);
}

void Main()
{
    // ...

    Foo bar;
    bar = null;

    Type myType = GetDeclaredType(bar);
    Console.Write(myType.Name);
}

Prints:

Foo

I posted this also at a similar topic, I hope it's of any use for you. ;-)

RJFalconer
  • 10,890
  • 5
  • 51
  • 66
herzmeister
  • 11,101
  • 2
  • 41
  • 51
15
if (o == null) return "null";
else return o.GetType().Name.ToLower();

simple solution for a simple problem :-p

fortran
  • 74,053
  • 25
  • 135
  • 175
  • I recommend at least adding `CultureInfo.InvariantCulture` to `ToLower`, this answer as it is now would fail the [Turkey test](https://blog.codinghorror.com/whats-wrong-with-turkey/) and get unexpected results in some cultures. – jrh May 25 '18 at 13:36
8

As others mention, you can't. This is actually a well-known issue with languages that allow pure null references to objects. One way to work around it is to use the "Null Object pattern". The basic idea is that instead of using null for empty references, you assign to it an instance of a "do nothing" object. For example:

public class Circle
{
    public virtual float Radius { get; set; }

    public Circle(float radius)
    {
        Radius = radius;
    }
}

public class NullCircle : Circle
{
    public override float Radius 
    { 
        get { return float.NaN; }
        set { }
    }

    public NullCircle() { }
}

You can then pass an instance of NullCircle instead of null and you will be able to test its type like in your code.

Hans Van Slooten
  • 2,257
  • 2
  • 19
  • 18
3

To the best of my knowledge you can't. Null indicates the absence of a value and is not distinct for different types.

Jeff Foster
  • 43,770
  • 11
  • 86
  • 103
2

Just expanding upon @Josh Einstein's answer.

Below are two extension methods to get the type of a variable even if it is currently set to null.

    /// <summary>
    /// Gets an object's type even if it is null.
    /// </summary>
    /// <typeparam name="T">The type of the object.</typeparam>
    /// <param name="that">The object being extended.</param>
    /// <returns>The objects type.</returns>
    public static Type GetTheType<T>(this T that)
    {
        return typeof(T);
    }

    /// <summary>
    /// Gets an object's type even if it is null.
    /// </summary>
    /// <param name="that">The object being extended.</param>
    /// <returns>The objects type.</returns>
    public static Type GetTheType(this object that)
    {
        if (that != null)
        {
            return that.GetType();
        }

        return null;
    }

Also, here are two simple unit tests to test the extension methods.

    /// <summary>
    /// Tests to make sure that the correct type is return.
    /// </summary>
    [Test(Description = "Tests to make sure that the correct type is return.")]
    public void Test_GetTheType()
    {
        var value = string.Empty;

        var theType = value.GetTheType();

        Assert.That(theType, Is.SameAs(typeof(string)));
    }

    /// <summary>
    /// Tests to make sure that the correct type is returned even if the value is null.
    /// </summary>
    [Test(Description = "Tests to make sure that the correct type is returned even if the value is null.")]
    public void Test_GetTheType_ReturnsTypeEvenIfValueIsNull()
    {
        string value = null;

        var theType = value.GetTheType();

        Assert.That(theType, Is.SameAs(typeof(string)));
    }

Edit Sorry, I forgot to mention that I was needing this exact same feature for a project I'm currently on. All credit still should go to @Josh Einstein :D

Tanner Watson
  • 260
  • 3
  • 9
  • I assume you meant that an implementer should choose one of the two extension methods, because using both is not necessary. De description of your second one is not correct: it doesn't return the type when it is null, instead, it returns null. – Abel Jan 12 '12 at 14:37
  • Abel, yeah, you are correct. The second method returns null. I'll edit my response this weekend. Sorry guys. :( – Tanner Watson Apr 18 '12 at 13:05
  • To me, this is another great example of where Test-Driven development completely fails. Sure, your code passes the tests you wrote, but the tests completely missed the point of the function, which is to return the RUNTIME Type of an object. Instead your function returns the COMPILE-TIME definition of the object, which is useless, because since it is known at compile-time, it might as well be defined as a constant. Take a look at my answer for more details. – drwatsoncode Dec 14 '12 at 05:26
2

There is no notion that a null string is different than a null Array is different than a null anything else. From inside your function, you cannot determine the type name.

More specifically, an instance of a reference class (internally) includes a "pointer" to the type information about the object. When the input is null, there is no such pointer so the type information does not exist.

DocMax
  • 12,094
  • 7
  • 44
  • 44
1

It is very frustrating that C# does not allow for such a determination to be made. And it is not akin to asking what cake you would have in an empty box - an object comprises two independent components - the "incarnation" of the object and the information on the class that was used to create the object. The fact that this information can't be accessed easily is an ommission on the part of the C#'s developers.

All you can do by way of determination is this rather crippling method:

void Method(object obj)
{
if(obj is int)
{
//obj is of the int type
}
else if(obj is SomeComplexType)
{
//obj is of the SomeComplexType type
}
}

So, you can see that even if the object is null, its type information is nevertheless travelling alongside the object, it is not lost, you just cant handily access it. But this is, to say the least, inconvenient.

Alex
  • 21
  • 1
  • An object does not comprise two independent components in the way that your describe. A reference has a type (in this case obj is of type object) and must reference an object of that type or a subtype. To determine what the true (runtime) type of the object is, the reference must be followed, and using the information associated with the instance of the object, the type is determined. You cannot follow a null reference. – Damien_The_Unbeliever Oct 21 '09 at 12:30
  • Also, Alex's code would not work if obj is null. The "is" operator in C# will always return false if the object is null. See http://msdn.microsoft.com/en-us/library/scekt9xw(v=vs.71).aspx – Anssssss Feb 22 '12 at 16:45
1

If you have an object by itself (let's say as an input parameter to a method with type object), with no definition or generic type, there is no way to find the type. The reason is simple, you cannot send message to (invoke any method on) the object to ask about the type.

There could be some other workarounds, as you see in some answers, like using generic types. In that case, you're not asking the Null object, you are asking the generic type for its type.

Amin Emami
  • 497
  • 2
  • 5
  • 12
0

Consider this code:

    public class MyClass1{}
    public class MyClass2{}

    public static void Test1()
    {
        MyClass1 one = null;
        MyClass2 two = (MyClass2) (object) one;

        one = new MyClass1();
        //invalid cast exception
        two = (MyClass2)(object) one;
    }

The runtime-type of a null instance is object, at least from a type-safety point of view.

Amy B
  • 108,202
  • 21
  • 135
  • 185