¿Cuál es la forma correcta de comprobar los valores nulos?


Me encanta el operador null-coalescing porque hace que sea fácil asignar un valor predeterminado para tipos nullables.

 int y = x ?? -1;

Eso es genial, excepto si necesito hacer algo simple con x. Por ejemplo, si quiero comprobar Session, entonces generalmente termino teniendo que escribir algo más detallado.

Desearía poder hacer esto:

string y = Session["key"].ToString() ?? "none";

Pero no se puede porque el .ToString() se llama antes de la comprobación null por lo que falla si Session["key"] es null. Termino haciendo esto:

string y = Session["key"] == null ? "none" : Session["key"].ToString();

It funciona y es mejor, en mi opinión, que la alternativa de tres líneas:

string y = "none";
if (Session["key"] != null)
    y = Session["key"].ToString();

Aunque eso funcione, todavía tengo curiosidad si hay una mejor manera. Parece que no importa lo que siempre tengo que hacer referencia Session["key"] dos veces; una vez para el cheque, y otra vez para la asignación. Alguna idea?

Author: Chev, 2012-03-20

10 answers

¿Qué pasa con

string y = (Session["key"] ?? "none").ToString();
 183
Author: BlackBear,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-20 14:12:47

Si haces esto con frecuencia específicamente con ToString() entonces podrías escribir un método de extensión:

public static string NullPreservingToString(this object input)
{
    return input == null ? null : input.ToString();
}

...

string y = Session["key"].NullPreservingToString() ?? "none";

O un método que toma un valor predeterminado, por supuesto:

public static string ToStringOrDefault(this object input, string defaultValue)
{
    return input == null ? defaultValue : input.ToString();
}

...

string y = Session["key"].ToStringOrDefault("none");
 130
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-20 19:02:31

También puedes usar as, que produce null si la conversión falla:

Session["key"] as string ?? "none"

Esto regresaría "none" incluso si alguien rellenara un int en Session["key"].

 21
Author: Andomar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-07 02:52:24

Si siempre será un string, puedes lanzar:

string y = (string)Session["key"] ?? "none";

Esto tiene la ventaja de quejarse en lugar de ocultar el error si alguien mete un int o algo en Session["key"]. ;)

 14
Author: Ry-,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-21 14:46:09

Todas las soluciones sugeridas son buenas, y responde a la pregunta; así que esto es solo para extenderlo un poco. Actualmente, la mayoría de las respuestas solo tratan con validación nula y tipos de cadena. Podría extender el objeto StateBag para incluir un método genérico GetValueOrDefault, similar a la respuesta publicada por Jon Skeet.

Un método de extensión genérico simple que acepta una cadena como clave, y luego type comprueba el objeto de sesión. Si el objeto es null o no es del mismo tipo, se devuelve el valor predeterminado, de lo contrario, el valor de la sesión se devuelve fuertemente escrito.

Algo como esto

/// <summary>
/// Gets a value from the current session, if the type is correct and present
/// </summary>
/// <param name="key">The session key</param>
/// <param name="defaultValue">The default value</param>
/// <returns>Returns a strongly typed session object, or default value</returns>
public static T GetValueOrDefault<T>(this HttpSessionState source, string key, T defaultValue)
{
    // check if the session object exists, and is of the correct type
    object value = source[key]
    if (value == null || !(value is T))
    {
        return defaultValue;
    }

    // return the session object
    return (T)value;
}
 10
Author: Richard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-21 09:58:48

Usamos un método llamado NullOr.

Uso

// Call ToString() if it’s not null, otherwise return null
var str = myObj.NullOr(obj => obj.ToString());

// Supply default value for when it’s null
var str = myObj.NullOr(obj => obj.ToString()) ?? "none";

// Works with nullable return values, too —
// this is properly typed as “int?” (nullable int)
// even if “Count” is just int
var count = myCollection.NullOr(coll => coll.Count);

// Works with nullable input types, too
int? unsure = 47;
var sure = unsure.NullOr(i => i.ToString());

Fuente

/// <summary>Provides a function delegate that accepts only value types as return types.</summary>
/// <remarks>This type was introduced to make <see cref="ObjectExtensions.NullOr{TInput,TResult}(TInput,FuncStruct{TInput,TResult})"/>
/// work without clashing with <see cref="ObjectExtensions.NullOr{TInput,TResult}(TInput,FuncClass{TInput,TResult})"/>.</remarks>
public delegate TResult FuncStruct<in TInput, TResult>(TInput input) where TResult : struct;

/// <summary>Provides a function delegate that accepts only reference types as return types.</summary>
/// <remarks>This type was introduced to make <see cref="ObjectExtensions.NullOr{TInput,TResult}(TInput,FuncClass{TInput,TResult})"/>
/// work without clashing with <see cref="ObjectExtensions.NullOr{TInput,TResult}(TInput,FuncStruct{TInput,TResult})"/>.</remarks>
public delegate TResult FuncClass<in TInput, TResult>(TInput input) where TResult : class;

/// <summary>Provides extension methods that apply to all types.</summary>
public static class ObjectExtensions
{
    /// <summary>Returns null if the input is null, otherwise the result of the specified lambda when applied to the input.</summary>
    /// <typeparam name="TInput">Type of the input value.</typeparam>
    /// <typeparam name="TResult">Type of the result from the lambda.</typeparam>
    /// <param name="input">Input value to check for null.</param>
    /// <param name="lambda">Function to apply the input value to if it is not null.</param>
    public static TResult NullOr<TInput, TResult>(this TInput input, FuncClass<TInput, TResult> lambda) where TResult : class
    {
        return input == null ? null : lambda(input);
    }

    /// <summary>Returns null if the input is null, otherwise the result of the specified lambda when applied to the input.</summary>
    /// <typeparam name="TInput">Type of the input value.</typeparam>
    /// <typeparam name="TResult">Type of the result from the lambda.</typeparam>
    /// <param name="input">Input value to check for null.</param>
    /// <param name="lambda">Function to apply the input value to if it is not null.</param>
    public static TResult? NullOr<TInput, TResult>(this TInput input, Func<TInput, TResult?> lambda) where TResult : struct
    {
        return input == null ? null : lambda(input);
    }

    /// <summary>Returns null if the input is null, otherwise the result of the specified lambda when applied to the input.</summary>
    /// <typeparam name="TInput">Type of the input value.</typeparam>
    /// <typeparam name="TResult">Type of the result from the lambda.</typeparam>
    /// <param name="input">Input value to check for null.</param>
    /// <param name="lambda">Function to apply the input value to if it is not null.</param>
    public static TResult? NullOr<TInput, TResult>(this TInput input, FuncStruct<TInput, TResult> lambda) where TResult : struct
    {
        return input == null ? null : lambda(input).Nullable();
    }
}
 7
Author: Timwi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-22 15:32:26

Mi preferencia, por un solo off, sería usar un cast seguro para string en caso de que el objeto almacenado con la clave no sea uno. Usar ToString() puede no tener los resultados que desea.

var y = Session["key"] as string ?? "none";

Como dice @Jon Skeet, si te encuentras haciendo esto mucho, un método de extensión o, mejor aún, tal vez un método de extensión junto con una clase SessionWrapper fuertemente mecanografiada. Incluso sin el método de extensión, la envoltura fuertemente mecanografiada podría ser una buena idea.

public class SessionWrapper
{
    private HttpSessionBase Session { get; set; }

    public SessionWrapper( HttpSessionBase session )
    {
        Session = session;
    }

    public SessionWrapper() : this( HttpContext.Current.Session ) { }

    public string Key
    {
         get { return Session["key"] as string ?? "none";
    }

    public int MaxAllowed
    {
         get { return Session["maxAllowed"] as int? ?? 10 }
    }
}

Utilizado como

 var session = new SessionWrapper(Session);

 string key = session.Key;
 int maxAllowed = session.maxAllowed;
 6
Author: tvanfosson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-20 19:12:20

Crear una función auxiliar

public static String GetValue( string key, string default )
{
    if ( Session[ key ] == null ) { return default; }
    return Session[ key ].toString();
}


string y = GetValue( 'key', 'none' );
 3
Author: scibuff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-20 14:14:02

La respuesta de Skeet es la mejor - en particular, creo que la suya ToStringOrNull() es bastante elegante y se adapta mejor a sus necesidades. Quería añadir una opción más a la lista de métodos de extensión:

Devuelve el objeto original o el valor de cadena predeterminado para null :

// Method:
public static object OrNullAsString(this object input, string defaultValue)
{
    if (defaultValue == null)
        throw new ArgumentNullException("defaultValue");
    return input == null ? defaultValue : input;
}

// Example:
var y = Session["key"].OrNullAsString("defaultValue");

Use var para el valor devuelto, ya que volverá como el tipo de entrada original, solo como la cadena predeterminada cuando null

 2
Author: one.beat.consumer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-20 18:46:36

Este es mi pequeño tipo seguro "Elvis operator" para versiones de. NET que no son compatibles ?.

public class IsNull
{
    public static O Substitute<I,O>(I obj, Func<I,O> fn, O nullValue=default(O))
    {
        if (obj == null)
            return nullValue;
        else
            return fn(obj);
    }
}

El primer argumento es el objeto probado. La segunda es la función. Y tercero es el valor nulo. Así que para su caso:

IsNull.Substitute(Session["key"],s=>s.ToString(),"none");

También es muy útil para tipos nullables. Por ejemplo:

decimal? v;
...
IsNull.Substitute(v,v.Value,0);
....
 0
Author: Tomaz Stih,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-01 08:57:00