C # 6.0 Operador de Propagación Nula y Asignación de Propiedades


Esta pregunta ha sido completamente revisada en el interés de ser minuciosa en la explicación.

He notado lo que parece ser una limitación bastante pobre del operador de propagación nula en C# 6.0 en que no se puede llamar a property setters contra un objeto que ha sido propagado null (aunque se puede llamar a property getters contra un objeto que ha sido propagado null). Como se verá en el IL generado (que tengo reflejado en C#) , no hay nada que deba limitar la capacidad de llamar a los setters de propiedades usando propagación nula.

Para empezar, he creado una clase simple, con ambos métodos Get/Set de estilo Java, y una propiedad con acceso público getter/setter.

public class Person
{
    public Person(string name, DateTime birthday)
    {
        Name = name;
    }

    public string Name { get; set; }

    public void SetName(string name)
    {
        Name = name;
    }

    public string GetName()
    {
        return Name;
    }
}

He probado la capacidad de propagación nula en la siguiente clase de prueba.

public class Program
{
    public static void Main(string[] args)
    {
        Person person = new Person("Joe Bloggs", DateTime.Parse("01/01/1991"));

        // This line doesn't work - see documented error below
        person?.Name = "John Smith";

        person?.SetName("John Smith");

        string name = person?.Name;
    }
}

El lado izquierdo de una asignación debe ser una variable, propiedad o indexador.

Usted puede observe de esto sin embargo que la forma Java de establecer el nombre, llamando a SetName(...) funciona, y también puede notar que obtener el valor de una propiedad propagada null también funciona.

Echemos un vistazo al C# que se generó a partir de este código:

public static void Main(string[] args)
{
    Person person = new Person("Joe Bloggs", DateTime.Parse("01/01/1991"));
    if (person != null)
    {
        person.SetName("John Smith");
    }
    string arg_33_0 = (person != null) ? person.Name : null;
}

Observe que cuando se usa contra el método SetName, la propagación nula se transforma en una instrucción if directa, y que cuando se usa contra el getter de propiedades Name, se usa un operador ternario para obtener el valor de Name o null.

Una cosa que he notado aquí es la diferencia de comportamiento entre usar una instrucción if y usar el operador ternario: cuando se usa un setter, usar una instrucción if funcionaría, mientras que usar un operador ternario no lo haría.

public static void Main(string[] args)
{
    Person person = null;

    if (person != null)
    {
        person.Name = "John Smith";
    }

    person.Name = (person != null) ? "John Smith" : null;
}

En este ejemplo estoy usando tanto una instrucción if como el operador ternario para verificar si person es null antes de intentar asignar a su propiedad Name. la instrucción if funciona como se espera; la instrucción usando el ternario el operador falla, como se espera

Referencia de objeto no establecida en una instancia de un objeto.

En mi opinión, la limitación proviene de la capacidad de C# 6.0 para transformar la propagación nula en una declaración if o una expresión ternaria. Si se hubiera diseñado para usar solo instrucciones if, la asignación de propiedades funcionaría a través de la propagación nula.

Hasta ahora, no he visto un argumento convincente de por qué esto NO debería ser posible, por lo tanto, todavía estoy ¡buscando respuestas!

Author: simonalexander2005, 2015-09-11

3 answers

¡No eres el único! SLaks planteó esto como un problema

¿Por qué no puedo escribir código así?

Process.GetProcessById(2)?.Exited += delegate { };

Y después de que se cerró brevemente como "Por diseño"

El ?. Operador nunca produce un lvalue, por lo que esto es por diseño.

Alguien comentó que sería bueno para los configuradores de propiedades, así como para los controladores de eventos

Tal vez agregar también setters de propiedades en la solicitud como:

Object?.Prop = false;

Y se volvió a abrir como una solicitud de característica para C#7.

 22
Author: Rawling,
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
2017-05-23 10:29:40

No puede usar el operador de propagación nula de esta manera.

Este operador permite propagar valores nulos mientras se evalúa una expresión. No se puede usar como el objetivo de una asignación exactamente como sugiere el error.

Debe atenerse a la antigua comprobación de null:

if (a != null)
{
    a.Value = someValue;
}
 4
Author: i3arnon,
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
2015-09-11 09:03:39

Pruébalo así...

using System;

namespace TestCon
{
    class Program
    {
        public static void Main()
    {

        Person person = null;
        //Person person = new Person() { Name = "Jack" };

        //Using an "if" null check.
        if (person != null)
        {
            Console.WriteLine(person.Name);
            person.Name = "Jane";
            Console.WriteLine(person.Name);
        }

        //using a ternary null check.
        string arg = (person != null) ? person.Name = "John" : arg = null;
        //Remember the first statment after the "?" is what happens when true. False after the ":". (Just saying "john" is not enough)
        //Console.WriteLine(person.Name);

        if (arg == null)
        {
            Console.WriteLine("arg is null");
        }

        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
public class Person
{
    public string Name { get; set; }
}

}

 -2
Author: Jayrooi,
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
2015-09-23 02:31:19