Estaba leyendo sobre las nuevas características de las variables de salida en C # 7 aquí . Tengo dos preguntas:
Dice
También permitimos "descartes" como parámetros de salida, en forma de a
_
, para permitirle ignorar los parámetros que no le interesan:p.GetCoordinates(out var x, out _); // I only care about x
P: Supongo que esto es solo una información y no una característica nueva de C # 7 porque también podemos hacerlo en versiones anteriores a C # 7.0:
var _; if (Int.TryParse(str, out _)) ...
o me estoy perdiendo algo aquí?
Mi código da un error cuando hago lo mencionado en el mismo blog:
~Person() => names.TryRemove(id, out *);
*
no es un identificador válido. ¿Un descuido de Mads Torgersen, supongo?
out _
_
no es una variable, no la declaras y no puedes usarla por su nombre. Enint _
eso hay una variable.out _
, sinvar
. Devar
hecho, es lo mismo que antes.Console.WriteLine(_)
, esto no compilará alegando que no existe tal variable. Bastante raro. Aún más: si hace algo como_ = SomeMethodCall()
, esto será reemplazado por soloSomeMethodCall()
en código compilado. Entonces, después de todo, todavía no puedes usar esa variable en ningún sentido significativo.Respuestas:
Los descartes , en C # 7, se pueden usar siempre que se declare una variable, para, como sugiere el nombre, descartar el resultado. Entonces, un descarte se puede usar sin variables:
p.GetCoordinates(out var x, out _);
y se puede usar para descartar el resultado de una expresión:
_ = 42;
En el ejemplo,
p.GetCoordinates(out var x, out _); _ = 42;
No se
_
está introduciendo ninguna variable`` . Solo hay dos casos en los que se utiliza un descarte.Sin embargo, si
_
existe un identificador en el alcance, los descartes no se pueden utilizar:var _ = 42; _ = "hello"; // error - a string cannot explicitly convert from string to int
La excepción a esto es cuando
_
se utiliza una variable como variable de salida. En este caso, el compilador ignora el tipo ovar
y lo trata como un descarte:if (p.GetCoordinates(out double x, out double _)) { _ = "hello"; // works fine. Console.WriteLine(_); // error: _ doesn't exist in this context. }
Tenga en cuenta que esto solo ocurre si, en este caso, se usa
out var _
oout double _
. Solo useout _
y luego se trata como una referencia a una variable existente_
, si está dentro del alcance, por ejemplo:string _; int.TryParse("1", out _); // complains _ is of the wrong type
Finalmente, la
*
notación se propuso al principio de las discusiones sobre los descartes, pero se abandonó_
debido a que esta última es una notación más utilizada en otros idiomas .fuente
_
debido a que este último es un ...'_ = 42
"descartar [s] el resultado de la expresión" es engañoso, porque en_ = 42
sí mismo es una expresión con el valor42
, por lo que no hay un descarte real. Todavía hay una diferencia porque_ = 42;
también es una declaración, mientras42;
que no lo es, lo que importa en algunos contextos._ = 42
no muestra cuál es el punto de este descarte, es decir, cuando necesitaría "no almacenar" una expresión, pero evaluarla de todos modos, ya que generalmente puede evaluar un (no trivial , útil) expresión muy bien sin almacenarla. Yo mismo no puedo pensar inmediatamente en un ejemplo útil (y no sé si hay uno, o si esto es solo el resultado de la coherencia en la gramática).Otro ejemplo del operador de descarte
_
en C # 7 es hacer coincidir el patrón con una variable de tipoobject
en unaswitch
declaración, que se agregó recientemente en C # 7:Código:
static void Main(string[] args) { object x = 6.4; switch (x) { case string _: Console.WriteLine("it is string"); break; case double _: Console.WriteLine("it is double"); break; case int _: Console.WriteLine("it is int"); break; default: Console.WriteLine("it is Unknown type"); break; } // end of main method }
Este código coincidirá con el tipo y descartará la variable pasada al
case ... _
.fuente
Para mas curiosos
Considere el siguiente fragmento
static void Main(string[] args) { //.... int a; int b; Test(out a, out b); Test(out _, out _); //.... } private static void Test(out int a, out int b) { //... }
Esto es lo que está pasando:
... 13: int a; 14: int b; 15: 16: Test(out a, out b); 02340473 lea ecx,[ebp-40h] 02340476 lea edx,[ebp-44h] 02340479 call 02340040 0234047E nop 17: Test(out _, out _); 0234047F lea ecx,[ebp-48h] 02340482 lea edx,[ebp-4Ch] 02340485 call 02340040 0234048A nop ...
Como puede ver detrás de escena, las dos llamadas hacen lo mismo.
Como señaló @ Servé Laurijssen, lo bueno es que no tiene que declarar previamente las variables, lo cual es útil si no está interesado en algunos valores.
fuente
Respecto a la primera pregunta
La novedad es que ya no tienes que declarar
_
dentro o fuera de la expresión y solo puedes escribirint.TryParse(s, out _);
Intente hacer este trazador de líneas previo a C # 7:
private void btnDialogOk_Click_1(object sender, RoutedEventArgs e) { DialogResult = int.TryParse(Answer, out _); }
fuente
SomeMethod(out _, out _, out three)
tiene 3 parámetros de salida, pero estoy desechando los dos primeros sin tener que crear variables como,unused1, unused2
etc.if (SomeMethod(out _, out _, out _)) _ = 5;
¿A qué_
se refiere?_
variable en absoluto, incluso si usaraout var _
. Parece que el guión bajo tiene mayúsculas especiales para descartar el resultado.En C # 7.0 (Visual Studio 2017 alrededor de marzo de 2017), los descartes se admiten en las asignaciones en los siguientes contextos:
Otras notas útiles
Ejemplo simple: aquí no queremos usar el primer y segundo parámetro y solo necesitamos el tercer parámetro
Ejemplo avanzado en caso de interruptor que utilizó también la coincidencia de patrones de caso de interruptor moderno ( fuente )
switch (exception) { case ExceptionCustom exceptionCustom: //do something unique //... break; case OperationCanceledException _: //do something else here and we can also cast it //... break; default: logger?.Error(exception.Message, exception); //.. break;
}
fuente
var _; if (Int.TryParse(str, out _))
Eso no es lo mismo.
Tu código está realizando una asignación.
En C # 7.0 _ no es una variable, le dice al compilador que descarte el valor
(a menos que haya declarado _ como una variable ... si lo hace, la variable se usa en lugar del símbolo de descarte)
Ejemplo: puede usar _ como una picadura y un int en la misma línea de código :
string a; int b; Test(out a, out b); Test(out _, out _); //... void Test(out string a, out int b) { //... }
fuente