.NET : How do you get the Type of a null object?

24,760

Solution 1

So is there any way to get the type of an object that is set to null? I would think there would have to be a way to know what type a storage location is without it being assigned anything.

Not necessarily. The best that you can say is that it is an object. A null reference does not point to any storage location, so there is no metadata from which it can make that determination.

The best that you could do is change it to be more generic, as in:

public void GetParameterValue<T>(out T destination)
{
    object paramVal = "Blah";
    destination = default(T);
    destination = Convert.ChangeType(paramVal, typeof(T));
}

The type of T can be inferred, so you shouldn't need to give a type parameter to the method explicitly.

Solution 2

It's possible if you don't mind declaring your method as a generic. Try this.

class Program
{
    public static void GetParameterValue<T>(out T destination)
    {
        Console.WriteLine("typeof(T)=" + typeof(T).Name);
        destination = default(T);
    }
    static void Main(string[] args)
    {
        string s;
        GetParameterValue(out s);
        int i;
        GetParameterValue(out i);
    }
}

Solution 3

The following extension method returns the type of its parameter as it was declared, regardless of its contents:

using System;

namespace MyNamespace
{
    public static class Extensions
    {
        /// <summary>
        /// Gets the declared type of the specified object.
        /// </summary>
        /// <typeparam name="T">The type of the object.</typeparam>
        /// <param name="obj">The object.</param>
        /// <returns>
        /// A <see cref="Type"/> object representing type 
        /// <typeparamref name="T"/>; i.e., the type of <paramref name="obj"/> 
        /// as it was declared. Note that the contents of 
        /// <paramref name="obj"/> are irrelevant; if <paramref name="obj"/> 
        /// contains an object whose class is derived from 
        /// <typeparamref name="T"/>, then <typeparamref name="T"/> is 
        /// returned, not the derived type.
        /// </returns>
        public static Type GetDeclaredType<T>(
            this T obj )
        {
            return typeof( T );
        }
    }
}

Since this is an extension method, its argument can be a null reference, and all of the following works OK:

string myString = "abc";
object myObj = myString;
Type myObjType = myObj.GetDeclaredType();

string myNullString = null;
object myNullObj = myNullString;
Type myNullObjType = myNullObj.GetDeclaredType();

Note that myObjType and myNullObjType will both be set to System.Object, not System.String.

If you actually want the type of obj's contents when it's not null, then change the return line to:

return (obj != null) ? obj.GetType() : typeof( T );

Solution 4

Currently, you have no way of knowing what gets passed into the method. You can convert it into a generic method like this:

public void GetParameterValue<T>(out T destination)
{
   ...
}

Solution 5

The type of your destination variable is always System.Object. You could just return

Convert.ChangeType(paramVal, System.Object).
Share:
24,760
CodingWithSpike
Author by

CodingWithSpike

Senior Software Engineer / Software Architect. Currently focused on frontend web development and JavaScript. Actively practicing TDD, and focusing on improving "code quality" by being mindful of DRY and SOLID principals.

Updated on January 13, 2021

Comments

  • CodingWithSpike
    CodingWithSpike over 3 years

    I have a method with an out parameter that tries to do a type conversion. Basically:

    public void GetParameterValue(out object destination)
    {
        object paramVal = "I want to return this. could be any type, not just string.";
    
        destination = null; // default out param to null
        destination = Convert.ChangeType(paramVal, destination.GetType());
    }
    

    The problem is that usually someone would call this like:

    string output;
    GetParameterValue(output);
    

    This will fail because of:

    destination.GetType()
    

    destination is null, so we can't call .GetType() on it. We also can not call:

    typeof(destination)
    

    because destination is a variable name not a type name.

    So is there any way to get the type of an object that is set to null? I would think there would have to be a way to know what type a storage location is without it being assigned anything.


    Just to give a bit more info, I am trying to make a utility method that will grab the output parameters of an Oracle stored procedure. The issue is that DbParameter.Value is of type object.

    What would be ideal would be for the developers to do something like:

    string val = GetParameterValue("parameterName");
    

    The notable thing is that there is no casting of types. In practice, you don't know the lparam of the "equals", so I went with:

    string val;
    GetParameterValue("parameterName", out val);
    

    And figured within the method, I would know the destination type of the output variable. I guess that was a bad assumption. As an alternative, I also wrote the method:

    public T GetParameterValue<T>(string paramName)
    

    So the developers can do:

    string val = GetParameterValue<string>("parameterName");
    

    I find the explicit "string" declaration to be repetitive, especially since in practice, the destination if probably an object property and the oracle data type could change (think ORM):

    MyObj.SomeProp = GetParameterValue<MyObj.SomeProp.GetType()>("parameterName");
    

    But again, if MyObj.SomeProp is null, that .GetType() call fails. The VM has to know the type of MyObj.SomeProp, even when its null, right? or else how would it catch cast exceptions?


    To partially solve my own problem, I can do:

    MyObj.SomeProp = GetParameterValue<typeof(MyObj).GetField("SomeProp").GetType()>("parameterName");
    

    The whole idea was to not have to explicitly use the Type in more than one place, so that if the data type changes, it only has to be changed in the destination object (MyObj.SomeProp) and in the DB. There has to be a better way...