three proposal propagacion parametros operator operador dots c# roslyn c#-6.0

c# - proposal - Operador de propagación nulo y métodos de extensión



rest javascript (3)

Estuve mirando Visual Studio 14 CTP junto con C # 6.0 y jugando con el operador de propagación nula.

Sin embargo, no pude encontrar por qué el siguiente código no se compila. Las funciones aún no están documentadas, por lo que no estoy seguro de si esto es un error o los métodos de extensión simplemente no son compatibles con el ?. operador y el mensaje de error es engañoso.

class C { public object Get() { return null; } } class CC { } static class CCExtensions { public static object Get(this CC c) { return null; } } class Program { static void Main(string[] args) { C c = null; var cr = c?.Get(); //this compiles (Get is instance method) CC cc = null; var ccr = cc?.Get(); //this doesn''t compile Console.ReadLine(); } }

Mensaje de error es:

''ConsoleApplication1.CC'' no contiene una definición para ''Obtener'' y no se pudo encontrar ningún método de extensión ''Obtener'' aceptando un primer argumento de tipo ''ConsoleApplication1.CC'' (¿falta una directiva using o una referencia de ensamblado?)


El objetivo del operador de propagación nula es evitar la eliminación de un valor null .

Sin embargo, los métodos de extensión no deferencia su this parámetro, y de hecho se puede llamar perfectamente perfecto en valores null .
(aunque es probable que el método arroje una excepción si no espera null )

Por lo tanto, no estaría claro si una llamada al método de extensión nulo seguro saltaría la llamada o no.


No trabajo en el equipo de Roslyn, pero estoy bastante seguro de que esto es un error. Eché un vistazo al código fuente y puedo explicar lo que está sucediendo.

En primer lugar, no estoy de acuerdo con la respuesta de SLaks de que esto no es compatible porque los métodos de extensión no desreferencian su this parámetro. Es un reclamo infundado, considerando que no se menciona en ninguna de the discussions design . Además, la semántica del operador se convierte en algo que se parece más o menos al operador ternario ( (obj == null) ? null : obj.Member ), por lo que no hay realmente una buena razón por la cual no pueda ser respaldado en un sentido técnico . Quiero decir, cuando se reduce al código generado, realmente no hay diferencia en this implícito en un método de instancia y el explícito this en el método de extensión estática.

El mensaje de error es una buena pista de que se trata de un error, porque se queja de que el método no existe, cuando realmente lo hace. Puede haber probado esto eliminando el operador condicional de la llamada, utilizando en su lugar el operador de acceso de miembro y haciendo que el código se compile correctamente. Si esto fuera un uso ilegal del operador, recibiría un mensaje similar a este: error CS0023: Operator ''.'' cannot be applied to operand of type ''<type>'' error CS0023: Operator ''.'' cannot be applied to operand of type ''<type>'' .

El error es que cuando Binder intenta enlazar la sintaxis con los símbolos compilados, utiliza un método private static NameSyntax GetNameSyntax(CSharpSyntaxNode, out string) [link] que no puede devolver el nombre del método que se necesita cuando intenta enlazar la expresión de invocación (nuestra llamada a método).

Una posible solución es agregar una declaración de case adicional al interruptor en GetNameSyntax [link] siguiente manera (File: Compilers/CSharp/Source/Binder/Binder_Expressions.cs:2748 ):

// ... case SyntaxKind.MemberBindingExpression: return ((MemberBindingExpressionSyntax)syntax).Name; // ...

Esto probablemente se pasó por alto porque la sintaxis para llamar a los métodos de extensión como miembros, que usa el operador de acceso miembro, termina usando un conjunto diferente de sintaxis que cuando se usa con el operador de acceso miembro frente al operador de acceso condicional, específicamente, el ?. operador usa un MemberBindingExpressionSyntax que no se tuvo en cuenta para ese método GetNameSyntax .

Curiosamente, el nombre del método no se rellena para el primer var cr = c?.Get(); que compila Funciona, sin embargo, porque los miembros del grupo de métodos locales primero se encuentran para el tipo y se pasan a la llamada de BindInvocationExpression [ link ]. Cuando el método se está resolviendo (observe la llamada a ResolveDefaultMethodGroup [ link ] antes de intentar BindExtensionMethod [ link ]), primero verifica esos métodos y los encuentra. En el caso del método de extensión, intenta encontrar un método de extensión que coincida con el nombre del método que se pasó al método, que en este caso era una cadena vacía en lugar de Get , y hace que se muestre el error erróneo.

Con mi versión local de Roslyn con mi corrección de errores, obtengo un ensamblado compilado cuyo código se ve como (regenerado usando dotPeek):

internal class Program { private static void Main(string[] args) { C c1 = (C) null; object obj1 = c1 != null ? c1.Get() : (object) null; CC c2 = (CC) null; object obj2 = c2 != null ? CCExtensions.Get(c2) : (object) null; Console.ReadLine(); } }


Sí. Este es un error. Gracias por mencionar esto. Se supone que la muestra se compila y debe dar como resultado una invocación condicional de Get independientemente de si Get es una extensión o no.

Uso de "?." en cc? .Get () es una indicación de que la persona que llama quiere cc null-checked antes de seguir adelante. Incluso si Get pudiera manejar el nulo de alguna manera, la persona que llama no quiere que eso suceda.