Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics and nullable type

Say I have a method that takes an int as a string and returns the int if the parse succeeds or a null value otherwise.

    int? ParseValue(string intAsString)
    {
        int i;
        if (int.TryParse(intAsString, out i))
            return i;
        return null;
    }

How can this method be re-written so that it works not only with int?, but also long?, decimal? and DateTime? ?

like image 811
Phillip Ngan Avatar asked Dec 10 '09 04:12

Phillip Ngan


People also ask

Can a generic be null?

Also, we cannot simply return null from a generic method like in normal method. Below is the error that a generic method will throw if we are trying to return null. So, to return a null or default value from a generic method we can make use default().

What do you mean by nullable type?

Nullable types are a feature of some programming languages which allow a value to be set to the special value NULL instead of the usual possible values of the data type.

What are nullable types used for?

You typically use a nullable value type when you need to represent the undefined value of an underlying value type. For example, a Boolean, or bool , variable can only be either true or false . However, in some applications a variable value can be undefined or missing.

Can a string be nullable?

The nullable type can only be used with value types and not with reference types. So, we cannot use nullable with string.


1 Answers

It's funny you should mention it because I was messing around with something just like this the other day:

using System;
using System.Reflection;

static class Example
{
    public static Tuple<Boolean, T?> TryParse<T>(this String candidate)
        where T : struct
    {
        T? value = null;
        Boolean success = false;

        var parser = ParsingBinder<T>.GetParser();

        try 
        { 
                value = parser(candidate);
                success = true;
        } 
        catch (FormatException) { }

        return new Tuple<Boolean,T?>(success, value);
    }
}

static class ParsingBinder<T>
{
    static Func<String, T> parser;

    public static Func<String, T> GetParser()
    {
        if (parser == null)
                parser = getParser();

        return parser;
    }

    static Func<String, T> getParser()
    {
        MethodInfo methodInfo 
            = typeof(T).GetMethod(
                    "Parse", new [] { typeof(String) });

        if (methodInfo == null)
                throw new Exception(
                        "Unable to retrieve a \"Parse\" method for type.");

        return (Func<String, T>)Delegate
        .CreateDelegate(typeof(Func<String, T>), methodInfo);
    }
}

It is a similar approach but think of it like a better TryParse method that returns a Tuple<Boolean, T?> (this requires .NET 4). The first property of the tuple is a boolean value indicating the success or failure of the parsing attempt and the second property is a nullable value typed to the generic type argument that will be null if parsing fails and the value if parsing succeeds.

It works by using reflection to retrieve a static Parse(String) method from the generic type argument and invokes that method for the string that is passed in. I built it as an extension method to allow you to do stuff like this:

var intValue = "1234".TryParse<Int32>();
var doubleValue = "1234".TryParse<Double>();

Unfortunately this won't work on enums since they don't have the same signature for the parse method so you couldn't use this extension to parse an enum but it wouldn't be hard to hack this up to make a special case for enums.

One of the nice things about this approach is that the cost of retrieving the Parse method via reflection is only incurred on the first use since a static delegate is created for all subsequent uses.


One more thing - the only thing that is clunky about this approach is that there is no language extensions or syntactic sugar that would make this easy to work with. What I was hoping to achieve with this code was a less clunky way of using the standard TryParse methods that exist in the BCL.

I personally find this pattern rather ugly:

Int32 value;
if (Int32.TryParse(someString, out value))
    // do something with value

mainly because it requires a variable declaration ahead of time and the use of an out parameter. My approach above isn't really that much better:

var result = someString.TryParse<Int32>();
if (result.Item1)
    // do something with result.Item2

What would be really cool would be to see a C# language extension that was built to work with a Tuple<Boolean, T?> that would allow us to work with this type smoothly but I get the feeling the more I write about this that it doesn't really seem that feasible.

like image 168
Andrew Hare Avatar answered Sep 25 '22 23:09

Andrew Hare