CuriousBenjamin
CuriousBenjamin

Reputation: 719

Generic method to return Nullable Type values

I wrote below method with follwing requirement -

  1. input is xmlnode and attributeName
  2. return the value if it is found with the associated attribute name passed
  3. Where there is no value in attributeName being passed, it should return -

    3.1. For int -1 3.2. For Datetime DateTime.MinValue 3.3. For String, null 3.4. For bool, null

Below method fails for case 3.4.

public T AttributeValue<T>(XmlNode node, string attributeName)  
        {
            var value = new object();

            if (node.Attributes[attributeName] != null && !string.IsNullOrEmpty(node.Attributes[attributeName].Value))
            {
                value = node.Attributes[attributeName].Value;
            }
            else
            {

                if (typeof(T) == typeof(int))
                    value = -1;
                else if (typeof(T) == typeof(DateTime))
                    value = DateTime.MinValue;
                else if (typeof(T) == typeof(string))
                    value = null;
                else if (typeof(T) == typeof(bool))
                    value = null;



            }
            return (T)Convert.ChangeType(value, typeof(T));
        }

When change this to

public System.Nullable<T> AttributeValue<T>(XmlNode node, string attributeName) where T : struct 
        {
            var value = new object();

            if (node.Attributes[attributeName] != null && !string.IsNullOrEmpty(node.Attributes[attributeName].Value))
            {
                value = node.Attributes[attributeName].Value;
            }
            else
            {

                if (typeof(T) == typeof(int))
                    value = -1;
                else if (typeof(T) == typeof(DateTime))
                    value = DateTime.MinValue;
                else if (typeof(T) == typeof(string))
                    return null;
                else if (typeof(T) == typeof(bool))
                    return  null;



            }
            return (T?)Convert.ChangeType(value, typeof(T));
        }

It fails for string type i.e. case 3.3

Looking forward for some help.

Upvotes: 12

Views: 10930

Answers (3)

CuriousBenjamin
CuriousBenjamin

Reputation: 719

thanks for a number of replies, this is what I wrote and it works for me..

It returns null for the types.

public T AttributeValue<T>(XmlNode node, string attributeName)
        {
            object value = null;

            if (node.Attributes[attributeName] != null && !string.IsNullOrEmpty(node.Attributes[attributeName].Value))
                value = node.Attributes[attributeName].Value;

            if (typeof(T) == typeof(bool?) && value != null)
                value = (string.Compare(value.ToString(), "1", true) == 0).ToString();

            var t = typeof(T);
            t = Nullable.GetUnderlyingType(t) ?? t;

            return (value == null) ?
                default(T) : (T)Convert.ChangeType(value, t);
        }

I call it like this

    const string auditData = "<mydata><data><equipmentStatiticsData><userStatistics maxUsers='100' totalUsers='1' authUsers='0' pendingUsers='' adminAddedUsers='' xmlUsers='' internalDBUsers='' webUsers='' macUsers='' vpnUsers='' xUsers8021=''></userStatistics><equipmentStatistics cpuUseNow='14' cpuUse5Sec='1' cpuUse10Sec='1' cpuUse20Sec='1' ramTotal='31301632' ramUtilization ='1896448' ramBuffer='774144' ramCached='8269824' permStorageUse='24' tempStorageUse='24'></equipmentStatistics><authStatus  status='1'></authStatus></equipmentStatiticsData></data></mydata>";
    xmlDoc.LoadXml(auditData);
    var userStatsNode = xmlDoc.SelectSingleNode("/mydata/data/equipmentStatiticsData/userStatistics");


    var intNullable = AttributeValue<int?>(userStatsNode, "vpnUsers");
    var nullableBoolTrue = AttributeValue<bool?>(userStatsNode, "totalUsers");
    var nullableBoolFalse = AttributeValue<bool?>(userStatsNode, "authUsers");
    var nullableString = AttributeValue<string>(userStatsNode, "authUsers");
    var pendingUsersBoolNull = AttributeValue<bool?>(userStatsNode, "pendingUsers");
    var testAttribNullableNotFoundDateTime = AttributeValue<DateTime?>(userStatsNode, "testAttrib");
    var testAttrib1NullString = AttributeValue<string>(userStatsNode, "testAttrib");
    var maxUsersNullInt = AttributeValue<int?>(userStatsNode, "maxUsers");

it works well for me. thanks people...

Upvotes: 6

Chris Sinclair
Chris Sinclair

Reputation: 23208

You need to call your first code set using bool? not bool because null isn't a valid value for a non-nullable bool.

Your second code block fails because you can't use string for the generic type of Nullable<T> as it requires a value-type struct and string is a reference-type class.

You'll need to change your first method block to look for typeof(bool?) and call it with a nullable boolean type:

public T AttributeValue<T>(XmlNode node, string attributeName)  
{
    var value = new object();

    if (node.Attributes[attributeName] != null && !string.IsNullOrEmpty(node.Attributes[attributeName].Value))
    {
        value = node.Attributes[attributeName].Value;
    }
    else
    {
        if (typeof(T) == typeof(int))
            value = -1;
        else if (typeof(T) == typeof(DateTime))
            value = DateTime.MinValue;
        else if (typeof(T) == typeof(string))
            value = null;
        else if (typeof(T) == typeof(bool?))
            value = null;
    }

    var type = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
    return (T)Convert.ChangeType(value, type);
}

Then call it as:

bool? value = AttributeValue<bool?>(node, "myAttributeName");

You also need to do a check as Convert.ChangeType won't work for a nullable type. A quick fix from here resolves that. (it is included in the code above)

EDIT: Here's an improved/cleaned version of your method:

public T AttributeValue<T>(XmlNode node, string attributeName)  
{
    if (node.Attributes[attributeName] != null && !string.IsNullOrEmpty(node.Attributes[attributeName].Value))
    {
        var value = node.Attributes[attributeName].Value;
        var type = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
        return (T)Convert.ChangeType(value, type);
    }
    else
    {
        if (typeof(T) == typeof(int))
            return (T)(object)(-1);

        return default(T);
    }
}

You can add additional special cases for non-existing nodes, but all your cases except for int are already the default value for the types so just use default(T) instead.

Upvotes: 0

Daniel J.G.
Daniel J.G.

Reputation: 34992

For 3.4 you need to use bool? as the type for T, so you can return null.

Then you can use the default keyword for 3.3 and 3.4 (string and bool?). As per msdn it will return null for reference types and the default value for value types (like int or bool).

You can use it like

return default(T);

Upvotes: 7

Related Questions