Novedades de C# 7.0What's new in C# 7.0

C# 7.0 incorpora varias características nuevas al lenguaje C#:C# 7.0 adds a number of new features to the C# language:

  • Variables de outout variables
    • Puede declarar valores out insertados como argumentos en el método cuando se usen.You can declare out values inline as arguments to the method where they're used.
  • TuplasTuples
    • Puede crear tipos ligeros sin nombre que contengan varios campos públicos.You can create lightweight, unnamed types that contain multiple public fields. Los compiladores y las herramientas IDE comprenden la semántica de estos tipos.Compilers and IDE tools understand the semantics of these types.
  • DescartesDiscards
    • Los descartes son variables temporales y de solo escritura que se usan en argumentos cuando el valor asignado es indiferente.Discards are temporary, write-only variables used in assignments when you don't care about the value assigned. Son especialmente útiles al deconstruir tuplas y tipos definidos por el usuario, así como al realizar llamadas a métodos con parámetros out.They're most useful when deconstructing tuples and user-defined types, as well as when calling methods with out parameters.
  • Coincidencia de patronesPattern Matching
    • Puede crear la lógica de bifurcación en función de tipos y valores arbitrarios de los miembros de esos tipos.You can create branching logic based on arbitrary types and values of the members of those types.
  • Devoluciones y variables locales refref locals and returns
    • Las variables locales de método y los valores devueltos pueden ser referencias a otro almacenamiento.Method local variables and return values can be references to other storage.
  • Funciones localesLocal Functions
    • Puede anidar funciones en otras funciones para limitar su ámbito y visibilidad.You can nest functions inside other functions to limit their scope and visibility.
  • Más miembros con forma de expresiónMore expression-bodied members
    • La lista de miembros que se pueden crear con expresiones ha crecido.The list of members that can be authored using expressions has grown.
  • Expresiones throwthrow Expressions
    • Puede iniciar excepciones en construcciones de código que antes no se permitían porque throw era una instrucción.You can throw exceptions in code constructs that previously weren't allowed because throw was a statement.
  • Tipos de valor devueltos de async generalizadosGeneralized async return types
    • Los métodos declarados con el modificador async pueden devolver otros tipos además de Task y Task<T>.Methods declared with the async modifier can return other types in addition to Task and Task<T>.
  • Mejoras en la sintaxis de literales numéricosNumeric literal syntax improvements
    • Nuevos tokens mejoran la legibilidad de las constantes numéricas.New tokens improve readability for numeric constants.

En el resto de este artículo se proporciona información general sobre cada característica.The remainder of this article provides an overview of each feature. Para cada característica, conocerá el razonamiento subyacente.For each feature, you'll learn the reasoning behind it. Aprenderá la sintaxis.You'll learn the syntax. Puede explorar estas características en su entorno mediante la herramienta global dotnet try:You can explore these features in your environment using the dotnet try global tool:

  1. Instale la herramienta global dotnet-try.Install the dotnet-try global tool.
  2. Clone el repositorio dotnet/try-samples.Clone the dotnet/try-samples repository.
  3. Establezca el directorio actual en el subdirectorio csharp7 para el repositorio try-samples.Set the current directory to the csharp7 subdirectory for the try-samples repository.
  4. Ejecute dotnet try.Run dotnet try.

Variables outout variables

En esta versión se ha mejorado la sintaxis existente que admite parámetros out.The existing syntax that supports out parameters has been improved in this version. Ahora puede declarar variables out en la lista de argumentos de una llamada a método, en lugar de escribir una instrucción de declaración distinta:You can now declare out variables in the argument list of a method call, rather than writing a separate declaration statement:

if (int.TryParse(input, out int result))
    Console.WriteLine(result);
else
    Console.WriteLine("Could not parse input");

Para mayor claridad, puede que prefiera especificar el tipo de la variable out, tal y como se muestra anteriormente.You may want to specify the type of the out variable for clarity, as shown above. Pero el lenguaje admite el uso de una variable local con tipo implícito:However, the language does support using an implicitly typed local variable:

if (int.TryParse(input, out var answer))
    Console.WriteLine(answer);
else
    Console.WriteLine("Could not parse input");
  • El código es más fácil de leer.The code is easier to read.
    • Declare la variable out donde la use, no en otra línea anterior.You declare the out variable where you use it, not on another line above.
  • No es preciso asignar ningún valor inicial.No need to assign an initial value.
    • Al declarar la variable out cuando se usa en una llamada de método, no podrá usarla accidentalmente antes de que se asigne.By declaring the out variable where it's used in a method call, you can't accidentally use it before it is assigned.

TuplasTuples

C# ofrece una sintaxis enriquecida para clases y estructuras que se usa para explicar la intención del diseño.C# provides a rich syntax for classes and structs that is used to explain your design intent. Pero a veces esa sintaxis enriquecida requiere trabajo adicional con apenas beneficio.But sometimes that rich syntax requires extra work with minimal benefit. Puede que a menudo escriba métodos que requieren una estructura simple que contenga más de un elemento de datos.You may often write methods that need a simple structure containing more than one data element. Para admitir estos escenarios, se han agregado tuplas a C#.To support these scenarios tuples were added to C#. Las tuplas son estructuras de datos ligeros que contienen varios campos para representar los miembros de datos.Tuples are lightweight data structures that contain multiple fields to represent the data members. Los campos no se validan y no se pueden definir métodos propiosThe fields aren't validated, and you can't define your own methods

Nota

Las tuplas estaban disponibles antes de C# 7.0, pero no eran eficientes ni compatibles con ningún lenguaje.Tuples were available before C# 7.0, but they were inefficient and had no language support. Esto significaba que solo se podía hacer referencia a los elementos tupla como Item1, Item2, por ejemplo.This meant that tuple elements could only be referenced as Item1, Item2 and so on. C# 7.0 presenta la compatibilidad de lenguaje con las tuplas, que permite usar nombres semánticos en los campos de una tupla mediante tipos de tupla nuevos y más eficientes.C# 7.0 introduces language support for tuples, which enables semantic names for the fields of a tuple using new, more efficient tuple types.

Puede crear una tupla asignando un valor a cada miembro, y, opcionalmente, proporcionando nombres semánticos a cada uno de los miembros de la tupla:You can create a tuple by assigning a value to each member, and optionally providing semantic names to each of the members of the tuple:

(string Alpha, string Beta) namedLetters = ("a", "b");
Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");

La tupla namedLetters contiene campos denominados Alpha y Beta.The namedLetters tuple contains fields referred to as Alpha and Beta. Esos nombres solo existen en tiempo de compilación y no se conservan, por ejemplo, al inspeccionar la tupla mediante la reflexión en tiempo de ejecución.Those names exist only at compile time and aren't preserved, for example when inspecting the tuple using reflection at runtime.

En la asignación de una tupla, también pueden especificarse los nombres de los campos a la derecha de la asignación:In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment:

var alphabetStart = (Alpha: "a", Beta: "b");
Console.WriteLine($"{alphabetStart.Alpha}, {alphabetStart.Beta}");

Puede que a veces quiera desempaquetar los miembros de una tupla devueltos de un método.There may be times when you want to unpackage the members of a tuple that were returned from a method. Para ello, declare distintas variables para cada uno de los valores de la tupla.You can do that by declaring separate variables for each of the values in the tuple. Este desempaquetado se denomina deconstrucción de la tupla:This unpackaging is called deconstructing the tuple:

(int max, int min) = Range(numbers);
Console.WriteLine(max);
Console.WriteLine(min);

También puede proporcionar una deconstrucción similar para cualquier tipo de .NET.You can also provide a similar deconstruction for any type in .NET. Un método Deconstruct se escribe como un miembro de la clase.You write a Deconstruct method as a member of the class. Ese método Deconstruct proporciona un conjunto de argumentos out para cada una de las propiedades que quiere extraer.That Deconstruct method provides a set of out arguments for each of the properties you want to extract. Tenga en cuenta que esta clase Point proporciona un método deconstructor que extrae las coordenadas X e Y:Consider this Point class that provides a deconstructor method that extracts the X and Y coordinates:

   public class Point
   {
       public Point(double x, double y) 
           => (X, Y) = (x, y);

       public double X { get; }
       public double Y { get; }

       public void Deconstruct(out double x, out double y) =>
           (x, y) = (X, Y);
   }

Puede extraer los campos individuales asignando un Point a una tupla:You can extract the individual fields by assigning a Point to a tuple:

var p = new Point(3.14, 2.71);
(double X, double Y) = p;

Puede obtener más información sobre las tuplas en el artículo sobre tuplas.You can learn more in depth about tuples in the tuples article.

DescartesDiscards

Habitualmente, al deconstruir una tupla o realizar una llamada a un método mediante parámetros out, debe definir una variable con un valor indiferente y que no vaya a usar.Often when deconstructing a tuple or calling a method with out parameters, you're forced to define a variable whose value you don't care about and don't intend to use. C# agrega la compatibilidad con descartes para gestionar este tipo de escenarios.C# adds support for discards to handle this scenario. Un descarte es una variable de solo escritura con el nombre _ (el carácter de guion bajo). Puede asignar todos los valores que quiera descartar a una única variable.A discard is a write-only variable whose name is _ (the underscore character); you can assign all of the values that you intend to discard to the single variable. Un descarte se parece a una variable no asignada, aunque no puede usarse en el código (excepto la instrucción de asignación).A discard is like an unassigned variable; apart from the assignment statement, the discard can't be used in code.

Los descartes se admiten en los escenarios siguientes:Discards are supported in the following scenarios:

  • Al deconstruir tuplas o tipos definidos por el usuario.When deconstructing tuples or user-defined types.
  • Al realizar llamadas a métodos mediante parámetros out.When calling methods with out parameters.
  • En una operación de coincidencia de patrones con las instrucciones is y switch.In a pattern matching operation with the is and switch statements.
  • Como un identificador independiente cuando quiera identificar explícitamente el valor de una asignación como descarte.As a standalone identifier when you want to explicitly identify the value of an assignment as a discard.

En el ejemplo siguiente se define un método QueryCityDataForYears que devuelve una tupla de tipo 6 con datos de una ciudad correspondientes a dos años diferentes.The following example defines a QueryCityDataForYears method that returns a 6-tuple that contains data for a city for two different years. La llamada de método del ejemplo se refiere únicamente a los dos valores de rellenado que devuelve el método, por lo que trata los valores restantes de la tupla como descartes al deconstruirla.The method call in the example is concerned only with the two population values returned by the method and so treats the remaining values in the tuple as discards when it deconstructs the tuple.

using System;
using System.Collections.Generic;

public class Example
{
   public static void Main()
   {
       var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

       Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
   }
   
   private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
   {
      int population1 = 0, population2 = 0;
      double area = 0;
      
      if (name == "New York City") {
         area = 468.48; 
         if (year1 == 1960) {
            population1 = 7781984;
         }
         if (year2 == 2010) {
            population2 = 8175133;
         }
      return (name, area, year1, population1, year2, population2);
      }

      return ("", 0, 0, 0, 0, 0);
   }
}
// The example displays the following output:
//      Population change, 1960 to 2010: 393,149

Para obtener más información, vea Descartes.For more information, see Discards.

Detección de patronesPattern matching

La coincidencia de patrones es una característica que permite implementar la distribución de métodos en propiedades distintas al tipo de un objeto.Pattern matching is a feature that allows you to implement method dispatch on properties other than the type of an object. Probablemente ya esté familiarizado con la distribución de métodos en función del tipo de un objeto.You're probably already familiar with method dispatch based on the type of an object. En la programación orientada a objetos, los métodos virtuales y de invalidación proporcionan la sintaxis del lenguaje para implementar la distribución de métodos en función del tipo de un objeto.In object-oriented programming, virtual and override methods provide language syntax to implement method dispatching based on an object's type. Las clases base y derivadas proporcionan distintas implementaciones.Base and Derived classes provide different implementations. Las expresiones de coincidencia de patrones extienden este concepto para que se puedan implementar fácilmente patrones de distribución similares para tipos y elementos de datos que no se relacionan mediante una jerarquía de herencia.Pattern matching expressions extend this concept so that you can easily implement similar dispatch patterns for types and data elements that aren't related through an inheritance hierarchy.

La coincidencia de patrones admite expresiones is y switch.Pattern matching supports is expressions and switch expressions. Cada una de ellas habilita la inspección de un objeto y sus propiedades para determinar si el objeto cumple el patrón buscado.Each enables inspecting an object and its properties to determine if that object satisfies the sought pattern. Use la palabra clave when para especificar reglas adicionales para el patrón.You use the when keyword to specify additional rules to the pattern.

La expresión de patrón is extiende el conocido operador is para consultar el tipo de un objeto y asignar el resultado en una instrucción.The is pattern expression extends the familiar is operator to query an object about its type and assign the result in one instruction. En el código siguiente se comprueba si una variable es de tipo int y, si lo es, se agrega a la suma actual:The following code checks if a variable is an int, and if so, adds it to the current sum:

if (input is int count)
    sum += count;

En el pequeño ejemplo anterior se muestran las mejoras en la expresión is.The preceding small example demonstrates the enhancements to the is expression. Puede probar con tipos de valor y tipos de referencia, y asignar el resultado correcto a una nueva variable del tipo correcto.You can test against value types as well as reference types, and you can assign the successful result to a new variable of the correct type.

La expresión de coincidencia switch tiene una sintaxis conocida, basada en la instrucción switch que ya forma parte del lenguaje C#.The switch match expression has a familiar syntax, based on the switch statement already part of the C# language. La instrucción switch actualizada tiene varias construcciones nuevas:The updated switch statement has several new constructs:

  • El tipo de control de una expresión switch ya no se limita a tipos enteros, tipos Enum, string o a un tipo que acepta valores NULL correspondiente a uno de esos tipos.The governing type of a switch expression is no longer restricted to integral types, Enum types, string, or a nullable type corresponding to one of those types. Se puede usar cualquier tipo.Any type may be used.
  • Puede probar el tipo de la expresión switch en todas las etiquetas case.You can test the type of the switch expression in each case label. Como sucede con la expresión is, puede asignar una variable nueva a ese tipo.As with the is expression, you may assign a new variable to that type.
  • Puede agregar una cláusula when para probar más condiciones en esa variable.You may add a when clause to further test conditions on that variable.
  • Ahora el orden de las etiquetas case es importante.The order of case labels is now important. Se ejecuta la primera rama de la que se quiere obtener la coincidencia, mientras que el resto se omite.The first branch to match is executed; others are skipped.

En el código siguiente se muestran estas características:The following code demonstrates these new features:

public static int SumPositiveNumbers(IEnumerable<object> sequence)
{
    int sum = 0;
    foreach (var i in sequence)
    {
        switch (i)
        {
            case 0:
                break;
            case IEnumerable<int> childSequence:
            {
                foreach(var item in childSequence)
                    sum += (item > 0) ? item : 0;
                break;
            }
            case int n when n > 0:
                sum += n;
                break;
            case null:
                throw new NullReferenceException("Null found in sequence");
            default:
                throw new InvalidOperationException("Unrecognized type");
        }
    }
    return sum;
}
  • case 0: es el patrón de constante conocido.case 0: is the familiar constant pattern.
  • case IEnumerable<int> childSequence: es un patrón de tipo.case IEnumerable<int> childSequence: is a type pattern.
  • case int n when n > 0: es un patrón de tipo con una condición when adicional.case int n when n > 0: is a type pattern with an additional when condition.
  • case null: es el patrón NULL.case null: is the null pattern.
  • default: es el caso predeterminado conocido.default: is the familiar default case.

Puede obtener más información sobre la coincidencia de patrones en Coincidencia de patrones en C#.You can learn more about pattern matching in Pattern Matching in C#.

Devoluciones y variables locales refRef locals and returns

Esta característica habilita algoritmos que usan y devuelven referencias a variables definidas en otro lugar.This feature enables algorithms that use and return references to variables defined elsewhere. Por ejemplo, trabajar con matrices de gran tamaño y buscar una sola ubicación con determinadas características.One example is working with large matrices, and finding a single location with certain characteristics. El método siguiente devuelve una referencia a ese almacenamiento en la matriz:The following method returns a reference to that storage in the matrix:

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

Puede declarar el valor devuelto como un elemento ref y modificar ese valor en la matriz, como se muestra en el código siguiente:You can declare the return value as a ref and modify that value in the matrix, as shown in the following code:

ref var item = ref MatrixSearch.Find(matrix, (val) => val == 42);
Console.WriteLine(item);
item = 24;
Console.WriteLine(matrix[4, 2]);

El lenguaje C# tiene varias reglas que impiden el uso incorrecto de las variables locales y devoluciones de ref:The C# language has several rules that protect you from misusing the ref locals and returns:

  • Tendrá que agregar la palabra clave ref a la firma del método y a todas las instrucciones return de un método.You must add the ref keyword to the method signature and to all return statements in a method.
    • Esto evidencia que el método se devuelve por referencia a lo largo del método.That makes it clear the method returns by reference throughout the method.
  • Se puede asignar ref return a una variable de valor, o bien a una variable ref.A ref return may be assigned to a value variable, or a ref variable.
    • El autor de la llamada controla si se copia el valor devuelto o no.The caller controls whether the return value is copied or not. La omisión del modificador ref al asignar el valor devuelto indica que el autor de la llamada quiere una copia del valor, no una referencia al almacenamiento.Omitting the ref modifier when assigning the return value indicates that the caller wants a copy of the value, not a reference to the storage.
  • No se puede asignar un valor devuelto de método estándar a una variable local ref.You can't assign a standard method return value to a ref local variable.
    • No permite instrucciones como ref int i = sequence.Count();That disallows statements like ref int i = sequence.Count();
  • No se puede devolver un elemento ref a una variable cuya duración se extiende más allá de la ejecución del método.You can't return a ref to a variable whose lifetime doesn't extend beyond the execution of the method.
    • Esto significa que no se puede devolver una referencia a una variable local o a una variable con un ámbito similar.That means you can't return a reference to a local variable or a variable with a similar scope.
  • Las ref locales y las devoluciones no se pueden usar con métodos asíncronos.ref locals and returns can't be used with async methods.
    • El compilador no puede identificar si una variable a la que se hace referencia se ha establecido en su valor final en la devolución del método asíncrono.The compiler can't know if the referenced variable has been set to its final value when the async method returns.

La incorporación de variables locales ref y devoluciones de ref permite usar algoritmos que resultan más eficientes si se evita copiar los valores o se realizan operaciones de desreferencia varias veces.The addition of ref locals and ref returns enables algorithms that are more efficient by avoiding copying values, or performing dereferencing operations multiple times.

Agregar ref al valor devuelto es un cambio compatible con el origen.Adding ref to the return value is a source compatible change. El código existente se compila, pero el valor devuelto de referencia se copia cuando se asigna.Existing code compiles, but the ref return value is copied when assigned. Los autores de las llamadas deben actualizar el almacenamiento para el valor devuelto en una variable local ref para almacenar el valor devuelto como referencia.Callers must update the storage for the return value to a ref local variable to store the return as a reference.

Para más información, consulte el artículo sobre la palabra clave ref.For more information, see the ref keyword article.

Funciones localesLocal functions

Muchos diseños de clases incluyen métodos que se llaman desde una sola ubicación.Many designs for classes include methods that are called from only one location. Estos métodos privados adicionales mantienen cada método pequeño y centrado.These additional private methods keep each method small and focused. Las funciones locales permiten declarar métodos en el contexto de otro método.Local functions enable you to declare methods inside the context of another method. Las funciones locales facilitan que los lectores de la clase vean que el método local solo se llama desde el contexto en el que se declara.Local functions make it easier for readers of the class to see that the local method is only called from the context in which it is declared.

Hay dos casos de uso comunes para las funciones locales: métodos de iterador públicos y métodos asincrónicos públicos.There are two common use cases for local functions: public iterator methods and public async methods. Ambos tipos de métodos generan código que informa de errores más tarde de lo que los programadores podrían esperar.Both types of methods generate code that reports errors later than programmers might expect. En los métodos de iterador, las excepciones solo se observan al llamar a código que enumera la secuencia devuelta.In iterator methods, any exceptions are observed only when calling code that enumerates the returned sequence. En los métodos asincrónicos, las excepciones solo se observan cuando se espera al elemento Task devuelto.In async methods, any exceptions are only observed when the returned Task is awaited. En el ejemplo siguiente se muestra la separación de la validación de parámetros de la implementación de iteradores mediante una función local:The following example demonstrates separating parameter validation from the iterator implementation using a local function:

public static IEnumerable<char> AlphabetSubset3(char start, char end)
{
    if (start < 'a' || start > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
    if (end < 'a' || end > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

    if (end <= start)
        throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");

    return alphabetSubsetImplementation();

    IEnumerable<char> alphabetSubsetImplementation()
    {
        for (var c = start; c < end; c++)
            yield return c;
    }
}

La misma técnica se puede emplear con métodos async para asegurarse de que las excepciones derivadas de la validación de argumentos se inician antes de comenzar el trabajo asincrónico:The same technique can be employed with async methods to ensure that exceptions arising from argument validation are thrown before the asynchronous work begins:

public Task<string> PerformLongRunningWork(string address, int index, string name)
{
    if (string.IsNullOrWhiteSpace(address))
        throw new ArgumentException(message: "An address is required", paramName: nameof(address));
    if (index < 0)
        throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
    if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));

    return longRunningWorkImplementation();

    async Task<string> longRunningWorkImplementation()
    {
        var interimResult = await FirstWork(address);
        var secondResult = await SecondStep(index, name);
        return $"The results are {interimResult} and {secondResult}. Enjoy.";
    }
}

Nota

Algunos de los diseños que se admiten con funciones locales también se podrían realizar con expresiones lambda.Some of the designs that are supported by local functions could also be accomplished using lambda expressions. Aquellos que estén interesados pueden obtener más información sobre las diferenciasThose interested can read more about the differences

Más miembros con forma de expresiónMore expression-bodied members

En C# 6 se presentaron los miembros con forma de expresión para funciones de miembros y propiedades de solo lectura.C# 6 introduced expression-bodied members for member functions, and read-only properties. C# 7.0 amplía los miembros permitidos que pueden implementarse como expresiones.C# 7.0 expands the allowed members that can be implemented as expressions. En C# 7.0, se pueden implementar constructores, finalizadores y descriptores de acceso get y set en propiedades e indizadores.In C# 7.0, you can implement constructors, finalizers, and get and set accessors on properties and indexers. En el código siguiente se muestran ejemplos de cada uno:The following code shows examples of each:

// Expression-bodied constructor
public ExpressionMembersExample(string label) => this.Label = label;

// Expression-bodied finalizer
~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");

private string label;

// Expression-bodied get / set accessors.
public string Label
{
    get => label;
    set => this.label = value ?? "Default label";
}

Nota

En este ejemplo no se requiere un finalizador, pero se muestra para demostrar la sintaxis.This example does not need a finalizer, but it is shown to demonstrate the syntax. No debe implementar un finalizador en la clase salvo que sea necesario para liberar recursos no administrados.You should not implement a finalizer in your class unless it is necessary to release unmanaged resources. También debe plantearse el uso de la clase SafeHandle en lugar de administrar directamente los recursos no administrados.You should also consider using the SafeHandle class instead of managing unmanaged resources directly.

Estas nuevas ubicaciones para los miembros con forma de expresión representan un hito importante para el lenguaje C#: miembros de la comunidad que trabajan en el proyecto Roslyn de código abierto implementaron estas características.These new locations for expression-bodied members represent an important milestone for the C# language: These features were implemented by community members working on the open-source Roslyn project.

Cambiar un método a un miembro con cuerpo de expresión es un cambio compatible con un elemento binario.Changing a method to an expression bodied member is a binary compatible change.

Expresiones throwThrow expressions

En C#, throw siempre ha sido una instrucción.In C#, throw has always been a statement. Como throw es una instrucción, no una expresión, había construcciones de C# en las que no se podía usar.Because throw is a statement, not an expression, there were C# constructs where you couldn't use it. Incluyen expresiones condicionales, expresiones de fusión nulas y algunas expresiones lambda.These included conditional expressions, null coalescing expressions, and some lambda expressions. La incorporación de miembros con forma de expresión agrega más ubicaciones donde las expresiones throw resultarían útiles.The addition of expression-bodied members adds more locations where throw expressions would be useful. Para que pueda escribir cualquiera de estas construcciones, C# 7.0 presenta las expresiones throw.So that you can write any of these constructs, C# 7.0 introduces throw expressions.

Esta adición facilita la escritura de código más basado en expresiones.This addition makes it easier to write more expression-based code. No se necesitan instrucciones adicionales para la comprobación de errores.You don't need additional statements for error checking.

Tipos de valor devueltos de async generalizadosGeneralized async return types

La devolución de un objeto Task desde métodos asincrónicos puede presentar cuellos de botella de rendimiento en determinadas rutas de acceso.Returning a Task object from async methods can introduce performance bottlenecks in certain paths. Task es un tipo de referencia, por lo que su uso implica la asignación de un objeto.Task is a reference type, so using it means allocating an object. En los casos en los que un método declarado con el modificador async devuelva un resultado en caché o se complete sincrónicamente, las asignaciones adicionales pueden suponer un costo considerable de tiempo en secciones críticas para el rendimiento del código.In cases where a method declared with the async modifier returns a cached result, or completes synchronously, the extra allocations can become a significant time cost in performance critical sections of code. Esas asignaciones pueden resultar costosas si se producen en bucles ajustados.It can become costly if those allocations occur in tight loops.

La nueva característica de lenguaje implica que los tipos de valor devuelto de métodos asincrónicos no están limitados a Task, Task<T> y void.The new language feature means that async method return types aren't limited to Task, Task<T>, and void. El tipo devuelto debe seguir cumpliendo con el patrón asincrónico, lo que significa que debe haber un método GetAwaiter accesible.The returned type must still satisfy the async pattern, meaning a GetAwaiter method must be accessible. Como ejemplo concreto, se ha agregado el tipo ValueTask a .NET Framework para sacar partido de esta nueva característica del lenguaje:As one concrete example, the ValueTask type has been added to the .NET framework to make use of this new language feature:

public async ValueTask<int> Func()
{
    await Task.Delay(100);
    return 5;
}

Nota

Debe agregar el paquete de NuGet System.Threading.Tasks.Extensions para poder usar el tipo ValueTask<TResult>.You need to add the NuGet package System.Threading.Tasks.Extensions in order to use the ValueTask<TResult> type.

Esta mejora es especialmente útil para que los creadores de bibliotecas eviten la asignación de un elemento Task en código de rendimiento crítico.This enhancement is most useful for library authors to avoid allocating a Task in performance critical code.

Mejoras en la sintaxis de literales numéricosNumeric literal syntax improvements

La lectura incorrecta de constantes numéricas puede complicar la comprensión del código cuando se lee por primera vez.Misreading numeric constants can make it harder to understand code when reading it for the first time. Las máscaras de bits u otros valores simbólicos son propensos a malentendidos.Bit masks or other symbolic values are prone to misunderstanding. En C# 7.0 se incluyen dos nuevas características para escribir números de la manera más legible para el uso previsto: literales binarios y separadores de dígitos.C# 7.0 includes two new features to write numbers in the most readable fashion for the intended use: binary literals, and digit separators.

En aquellos casos en que cree máscaras de bits, o siempre que una representación binaria de un número aumente la legibilidad del código, escriba el número en formato binario:For those times when you're creating bit masks, or whenever a binary representation of a number makes the most readable code, write that number in binary:

public const int Sixteen =   0b0001_0000;
public const int ThirtyTwo = 0b0010_0000;
public const int SixtyFour = 0b0100_0000;
public const int OneHundredTwentyEight = 0b1000_0000;

El 0b al principio de la constante indica que el número está escrito como número binario.The 0b at the beginning of the constant indicates that the number is written as a binary number. Los números binarios pueden ser muy largos, por lo que a menudo resulta más fácil ver los patrones de bits si se introduce _ como separador de dígitos, como se ha mostrado antes en la constante binaria.Binary numbers can get long, so it's often easier to see the bit patterns by introducing the _ as a digit separator, as shown above in the binary constant. El separador de dígitos puede aparecer en cualquier parte de la constante.The digit separator can appear anywhere in the constant. En números de base 10, es habitual usarlo como separador de miles:For base 10 numbers, it is common to use it as a thousands separator:

public const long BillionsAndBillions = 100_000_000_000;

El separador de dígitos también se puede usar con tipos decimal, float y double:The digit separator can be used with decimal, float, and double types as well:

public const double AvogadroConstant = 6.022_140_857_747_474e23;
public const decimal GoldenRatio = 1.618_033_988_749_894_848_204_586_834_365_638_117_720_309_179M;

En conjunto, se pueden declarar constantes numéricas con mucha más legibilidad.Taken together, you can declare numeric constants with much more readability.