Escritura de código C# seguro y eficazWrite safe and efficient C# code

Las nuevas características de C# permiten escribir código seguro verificable con un mejor rendimiento.New features in C# enable you to write verifiable safe code with better performance. Si aplica estas técnicas cuidadosamente, habrá menos escenarios que requieran código no seguro.If you carefully apply these techniques, fewer scenarios require unsafe code. Estas características permiten usar con más facilidad las referencias a tipos de valor como argumentos de método y devoluciones de método.These features make it easier to use references to value types as method arguments and method returns. Si aplica estas técnicas de forma segura, minimizará la copia de tipos de valor.When done safely, these techniques minimize copying value types. Al usar tipos de valor, también podrá minimizar el número de asignaciones y transferencias de recolección de elementos no utilizados.By using value types, you can minimize the number of allocations and garbage collection passes.

En gran parte del código de ejemplo de este artículo se utilizan las características agregadas a la versión 7.2 de C#.Much of the sample code in this article uses features added in C# 7.2. Para poder usar esas características, tiene que configurar el proyecto para que use la versión 7.2 de C# o una posterior.To use those features, you must configure your project to use C# 7.2 or later. Para obtener más información sobre cómo establecer la versión del lenguaje, vea cómo configurar la versión del lenguaje.For more information on setting the language version, see configure the language version.

Este artículo se centra en las técnicas que se deben aplicar para administrar recursos de forma eficaz.This article focuses on techniques for efficient resource management. Una de las ventajas de utilizar tipos de valor es que a menudo evitan las asignaciones de montón.One advantage to using value types is that they often avoid heap allocations. Pero también hay una desventaja, que es que se copian por valor.The disadvantage is that they're copied by value. Este último aspecto dificulta la optimización de los algoritmos que funcionan en grandes cantidades de datos.This tradeoff makes it harder to optimize algorithms that operate on large amounts of data. Las nuevas características del lenguaje en C# 7.2 proporcionan mecanismos que permiten escribir código seguro y eficaz mediante referencias a tipos de valor.New language features in C# 7.2 provide mechanisms that enable safe efficient code using references to value types. Use estas características acertadamente para minimizar tanto las asignaciones como las operaciones de copia.Use these features wisely to minimize both allocations and copy operations. En este artículo se exploran estas nuevas características.This article explores those new features.

Este artículo se centra en estas técnicas de administración de recursos:This article focuses on the following resource management techniques:

  • Declare un valor readonly struct para indicar que un tipo es inmutable, lo cual permite al compilador ahorrar procesos de copia usando parámetros in.Declare a readonly struct to express that a type is immutable and enables the compiler to save copies when using in parameters.
  • Si un tipo no puede ser inmutable, declare miembros readonly de struct para indicar que el miembro no modifica el estado.If a type can't be immutable, declare struct members readonly to indicate that the member doesn't modify state.
  • Utilice una devolución ref readonly si el valor devuelto es un valor struct mayor que IntPtr.Size y la duración del almacenamiento es superior al método que devuelve el valor.Use a ref readonly return when the return value is a struct larger than IntPtr.Size and the storage lifetime is greater than the method returning the value.
  • Si el tamaño de un valor readonly struct es mayor que IntPtr.Size, deberá pasarlo como parámetro in por motivos de rendimiento.When the size of a readonly struct is bigger than IntPtr.Size, you should pass it as an in parameter for performance reasons.
  • No pase nunca un elemento struct como parámetro in a menos que se declare con el modificador readonly o que el método solo llame a los miembros de readonly de la estructura.Never pass a struct as an in parameter unless it's declared with the readonly modifier or the method calls only readonly members of the struct. Infringir esta guía puede afectar negativamente al rendimiento y podría provocar un comportamiento oculto.Violating this guidance may negatively affect performance and could lead to an obscure behavior.
  • Use un valor ref struct, o bien un valor readonly ref struct como Span<T> o ReadOnlySpan<T>, para que funcione con la memoria como secuencia de bytes.Use a ref struct, or a readonly ref struct such as Span<T> or ReadOnlySpan<T> to work with memory as a sequence of bytes.

Estas técnicas obligan a equilibrar dos objetivos contrapuestos en relación con las referencias y los valores.These techniques force you to balance two competing goals with regard to references and values. Las variables que son tipos de referencia contienen una referencia a la ubicación en la memoria.Variables that are reference types hold a reference to the location in memory. Las variables que son tipos de valor contienen directamente su valor.Variables that are value types directly contain their value. Estas son diferencias clave importantes para administrar recursos de memoria.These differences highlight the key differences that are important for managing memory resources. Los tipos de valor suelen copiarse cuando se pasan a un método o se devuelven desde uno.Value types are typically copied when passed to a method or returned from a method. Este comportamiento incluye la copia del valor de this al llamar a los miembros de un tipo de valor.This behavior includes copying the value of this when calling members of a value type. El costo de dicha copia está relacionado con el tamaño del tipo.The cost of the copy is related to the size of the type. Los tipos de referencia se asignan en el montón administrado.Reference types are allocated on the managed heap. Cada nuevo objeto requiere una nueva asignación, y se debe reclamar posteriormente.Each new object requires a new allocation, and subsequently must be reclaimed. Ambas operaciones llevan cierto tiempo.Both these operations take time. La referencia se copia cuando un tipo de referencia se pasa como argumento a un método o se devuelve desde un método.The reference is copied when a reference type is passed as an argument to a method or returned from a method.

En este artículo se utiliza el concepto de ejemplo siguiente de la estructura de punto 3D para explicar estas recomendaciones:This article uses the following example concept of the 3D-point structure to explain these recommendations:

public struct Point3D
{
    public double X;
    public double Y;
    public double Z;
}

Los distintos ejemplos utilizan implementaciones distintas de este concepto.Different examples use different implementations of this concept.

Declaración de valores struct de solo lectura para tipos de valor inmutablesDeclare readonly structs for immutable value types

Al declarar un valor struct utilizando el modificador readonly, se informa al compilador de que su intención es crear un tipo inmutable.Declaring a struct using the readonly modifier informs the compiler that your intent is to create an immutable type. El compilador aplica esa decisión de diseño con las siguientes reglas:The compiler enforces that design decision with the following rules:

  • Todos los miembro de campo deben ser readonly.All field members must be readonly
  • Todas las propiedades deben ser de solo lectura, incluidas las implementadas automáticamente.All properties must be read-only, including auto-implemented properties.

Estas dos reglas son suficientes para garantizar que ningún miembro de un valor readonly struct vaya a modificar el estado de ese valor struct.These two rules are sufficient to ensure that no member of a readonly struct modifies the state of that struct. El valor struct es inmutable.The struct is immutable. La estructura de Point3D se podría definir como un valor struct inmutable tal como se muestra en este ejemplo:The Point3D structure could be defined as an immutable struct as shown in the following example:

readonly public struct ReadonlyPoint3D
{
    public ReadonlyPoint3D(double x, double y, double z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }

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

Siga esta recomendación siempre que su intención de diseño sea crear un tipo de valor inmutable.Follow this recommendation whenever your design intent is to create an immutable value type. Cualquier mejora de rendimiento que se aplique es una ventaja adicional.Any performance improvements are an added benefit. El valor readonly struct expresa claramente su intención de diseño.The readonly struct clearly expresses your design intent.

Declaración de miembros de solo lectura cuando una estructura no pueda ser inmutableDeclare readonly members when a struct can't be immutable

En C# 8.0 y versiones posteriores, cuando un tipo de estructura es mutable, debe declarar los miembros que no provocan la mutación para que sean readonly.In C# 8.0 and later, when a struct type is mutable, you should declare members that don't cause mutation to be readonly. Por ejemplo, lo siguiente es una variación mutable de la estructura de punto 3D:For example, the following is a mutable variation of the 3D point structure:

public struct Point3D
{
    public Point3D(double x, double y, double z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }

    private double _x;
    public double X 
    { 
        readonly get { return _x;}; 
        set { _x = value; }
    }
    
    private double _y;
    public double Y 
    { 
        readonly get { return _y;}; 
        set { _y = value; }
    }

    private double _z;
    public double Z 
    { 
        readonly get { return _z;}; 
        set { _z = value; }
    }

    public readonly double Distance => Math.Sqrt(X * X + Y * Y + Z * Z);

    public readonly override string ToString() => $"{X, Y, Z }";
}

En el ejemplo anterior se muestran muchas de las ubicaciones en las que puede aplicar el modificador readonly: métodos, propiedades y descriptores de acceso de propiedad.The preceding sample shows many of the locations where you can apply the readonly modifier: methods, properties, and property accessors. Si usa propiedades implementadas automáticamente, el compilador agrega el modificador readonly al descriptor de acceso get para las propiedades de lectura y escritura.If you use auto-implemented properties, the compiler adds the readonly modifier to the get accessor for read-write properties. El compilador agrega el modificador readonly a las declaraciones de propiedad implementadas automáticamente para las propiedades con solo un descriptor de acceso get.The compiler adds the readonly modifier to the auto-implemented property declarations for properties with only a get accessor.

Agregar el modificador readonly a los miembros que no mutan el estado proporciona dos ventajas relacionadas.Adding the readonly modifier to members that don't mutate state provides two related benefits. En primer lugar, el compilador aplica su intención.First, the compiler enforces your intent. Ese miembro no puede mutar el estado de la estructura ni tener acceso a un miembro que no esté marcado también como readonly.That member can't mutate the struct's state, nor can it access a member that isn't also marked readonly. En segundo lugar, el compilador no crea copias defensivas de parámetros in al obtener acceso a un miembro de readonly.Second, the compiler won't create defensive copies of in parameters when accessing a readonly member. El compilador puede hacer que esta optimización sea segura, ya que garantiza que un miembro de readonly no modifique struct.The compiler can make this optimization safely because it guarantees that the struct is not modified by a readonly member.

Uso de instrucciones ref readonly return para grandes estructuras en la medida de lo posibleUse ref readonly return statements for large structures when possible

Puede devolver valores por referencia cuando el valor que se devuelva no sea local en el método de devolución.You can return values by reference when the value being returned isn't local to the returning method. Al devolverlos de esta forma, se copiará solo la referencia, no la estructura.Returning by reference means that only the reference is copied, not the structure. En el ejemplo siguiente, la propiedad Origin no puede usar ninguna devolución ref porque el valor que se está devolviendo es una variable local:In the following example, the Origin property can't use a ref return because the value being returned is a local variable:

public Point3D Origin => new Point3D(0,0,0);

Sin embargo, la siguiente definición de propiedad se puede devolver por referencia porque el valor devuelto es un miembro estático:However, the following property definition can be returned by reference because the returned value is a static member:

public struct Point3D
{
    private static Point3D origin = new Point3D(0,0,0);

    // Dangerous! returning a mutable reference to internal storage
    public ref Point3D Origin => ref origin;

    // other members removed for space
}

Como no quiere que los autores de llamada modifiquen el origen, debe devolver el valor según su readonly ref:You don't want callers modifying the origin, so you should return the value by readonly ref:

public struct Point3D
{
    private static Point3D origin = new Point3D(0,0,0);

    public static ref readonly Point3D Origin => ref origin;

    // other members removed for space
}

Al devolver ref readonly, se podrá ahorrar procesos de copia de estructuras mayores y conservar la inmutabilidad de los miembros de datos internos.Returning ref readonly enables you to save copying larger structures and preserve the immutability of your internal data members.

En el sitio de llamada, los autores de dicha llamada eligen utilizar la propiedad Origin como readonly ref o como valor:At the call site, callers make the choice to use the Origin property as a readonly ref or as a value:

var originValue = Point3D.Origin;
ref readonly var originReference = ref Point3D.Origin;

La primera asignación en el código anterior realiza una copia de la constante Origin y asigna esa copia.The first assignment in the preceding code makes a copy of the Origin constant and assigns that copy. La segunda asigna una referencia.The second assigns a reference. Tenga en cuenta que el modificador readonly debe formar parte de la declaración de la variable.Notice that the readonly modifier must be part of the declaration of the variable. No se puede modificar la referencia a la que alude.The reference to which it refers can't be modified. Los intentos de hacerlo generarán un error en tiempo de compilación.Attempts to do so result in a compile-time error.

El modificador readonly es necesario en la declaración de originReference.The readonly modifier is required on the declaration of originReference.

El compilador exige que el autor de una llamada no pueda modificar la referencia.The compiler enforces that the caller can't modify the reference. Los intentos de asignar el valor directamente generan un error en tiempo de compilación.Attempts to assign the value directly generate a compile-time error. Sin embargo, el compilador no puede saber si algún método de miembro modifica el estado del valor struct.However, the compiler can't know if any member method modifies the state of the struct. Para asegurarse de que el objeto no se modifique, el compilador crea una copia y llama a las referencias de miembro usando esa copia.To ensure that the object isn't modified, the compiler creates a copy and calls member references using that copy. Las modificaciones se realizan sobre esa copia defensiva.Any modifications are to that defensive copy.

Aplicación del modificador in en los parámetros readonly struct mayores que System.IntPtr.SizeApply the in modifier to readonly struct parameters larger than System.IntPtr.Size

La palabra clave in complementa las palabras clave ref y out existentes para pasar argumentos por referencia.The in keyword complements the existing ref and out keywords to pass arguments by reference. La palabra clave in especifica que se pasa el argumento por referencia, pero el método llamado no modifica el valor.The in keyword specifies passing the argument by reference, but the called method doesn't modify the value.

Esta novedad proporciona un vocabulario completo para expresar la intención del diseño.This addition provides a full vocabulary to express your design intent. Los tipos de valor se copian al pasarlos a un método llamado cuando no se especifica ninguno de los siguientes modificadores en la firma de método.Value types are copied when passed to a called method when you don't specify any of the following modifiers in the method signature. Cada uno de estos modificadores especifica que una variable se pasa por referencia, evitando la copia.Each of these modifiers specifies that a variable is passed by reference, avoiding the copy. Cada modificador expresa un propósito diferente:Each modifier expresses a different intent:

  • out: este método establece el valor del argumento utilizado como este parámetro.out: This method sets the value of the argument used as this parameter.
  • ref: este método puede establecer el valor del argumento utilizado como este parámetro.ref: This method may set the value of the argument used as this parameter.
  • in: este método no modifica el valor del argumento utilizado como este parámetro.in: This method doesn't modify the value of the argument used as this parameter.

Agregue el modificador in para pasar un argumento por referencia y declare la intención del diseño de pasar argumentos por referencia para evitar la copia innecesaria.Add the in modifier to pass an argument by reference and declare your design intent to pass arguments by reference to avoid unnecessary copying. No pretende modificar el objeto usado como ese argumento.You don't intend to modify the object used as that argument.

A menudo, esta práctica mejora el rendimiento de los tipos de valor de solo lectura que son mayores que IntPtr.Size.This practice often improves performance for readonly value types that are larger than IntPtr.Size. En el caso de los tipos simples (sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool y enum), las posibles mejoras con respecto al rendimiento son mínimas.For simple types (sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal and bool, and enum types), any potential performance gains are minimal. De hecho, el rendimiento puede empeorar al usar un parámetro de paso por referencia para los tipos menores que IntPtr.Size.In fact, performance may degrade by using pass-by-reference for types smaller than IntPtr.Size.

El código siguiente muestra un ejemplo de un método que calcula la distancia entre dos puntos en un espacio 3D.The following code shows an example of a method that calculates the distance between two points in 3D space.

private static double CalculateDistance(in Point3D point1, in Point3D point2)
{
    double xDifference = point1.X - point2.X;
    double yDifference = point1.Y - point2.Y;
    double zDifference = point1.Z - point2.Z;

    return Math.Sqrt(xDifference * xDifference + yDifference * yDifference + zDifference * zDifference);
}

Los argumentos son dos estructuras que contienen cada una de ellas tres valores dobles.The arguments are two structures that each contain three doubles. Un valor doble tiene 8 bytes, por lo que cada argumento es de 24 bytes.A double is 8 bytes, so each argument is 24 bytes. Al especificar el modificador in, se pasa una referencia de 4 u 8 bytes a esos argumentos, en función de la arquitectura de la máquina.By specifying the in modifier, you pass a 4 byte or 8-byte reference to those arguments, depending on the architecture of the machine. La diferencia de tamaño es pequeña, pero aumenta rápidamente cuando la aplicación llama a este método en un bucle ajustado con muchos valores diferentes.The difference in size is small, but it adds up when your application calls this method in a tight loop using many different values.

El modificador in complementa también a out y ref de otras maneras.The in modifier complements out and ref in other ways as well. No puede crear sobrecargas de un método que difiere solo en cuanto a la presencia de in, out o ref.You can't create overloads of a method that differ only in the presence of in, out, or ref. Estas nuevas reglas extienden el mismo comportamiento que siempre se ha definido para los parámetros out y ref.These new rules extend the same behavior that had always been defined for out and ref parameters. Como en el caso de los modificadores out y ref, no se aplica la conversión boxing a los tipos de valor porque se aplica el modificador in.Like the out and ref modifiers, value types aren't boxed because the in modifier is applied.

El modificador in se puede aplicar a cualquier miembro que toma parámetros: métodos, delegados, expresiones lambda, funciones locales, indexadores u operadores.The in modifier may be applied to any member that takes parameters: methods, delegates, lambdas, local functions, indexers, operators.

Otra de las características de los parámetros in es que se pueden usar valores literales o constantes para el argumento en un parámetro in.Another feature of in parameters is that you may use literal values or constants for the argument to an in parameter. Además, a diferencia de un parámetro ref o out, no es necesario aplicar el modificador in en el sitio de llamada.Also, unlike a ref or out parameter, you don't need to apply the in modifier at the call site. El código siguiente muestra dos ejemplos de llamada al método CalculateDistance.The following code shows you two examples of calling the CalculateDistance method. El primero usa dos variables locales pasadas por referencia.The first uses two local variables passed by reference. El segundo incluye una variable temporal creada como parte de la llamada al método.The second includes a temporary variable created as part of the method call.

var distance = CalculateDistance(pt1, pt2);
var fromOrigin = CalculateDistance(pt1, new Point3D());

El compilador tiene varias maneras de aplicar la naturaleza de solo lectura de un argumento in.There are several ways in which the compiler enforces the read-only nature of an in argument. En primer lugar, el método llamado no se puede asignar directamente a un parámetro in.First of all, the called method can't directly assign to an in parameter. No se puede asignar directamente a ningún campo de un parámetro in cuando el valor es un tipo struct.It can't directly assign to any field of an in parameter when that value is a struct type. Además, no se puede pasar un parámetro in a ningún método que exija el modificador ref o out.In addition, you can't pass an in parameter to any method using the ref or out modifier. Estas reglas se aplican a cualquier campo de un parámetro in, siempre que el campo sea un tipo struct y el parámetro también sea un tipo struct.These rules apply to any field of an in parameter, provided the field is a struct type and the parameter is also a struct type. De hecho, estas reglas se aplican para varios niveles de acceso a miembros, siempre que los tipos en todos los niveles de acceso a miembros sean structs.In fact, these rules apply for multiple layers of member access provided the types at all levels of member access are structs. El compilador exige que los tipos struct que se pasan como argumentos in y sus miembros struct sean variables de solo lectura cuando se usan como argumentos para otros métodos.The compiler enforces that struct types passed as in arguments and their struct members are read-only variables when used as arguments to other methods.

El uso de parámetros in puede evitar los posibles costos de rendimiento que conlleva realizar copias.The use of in parameters can avoid the potential performance costs of making copies. No cambia la semántica de ninguna llamada al método.It doesn't change the semantics of any method call. Por lo tanto, no es necesario especificar el modificador in en el sitio de llamada.Therefore, you don't need to specify the in modifier at the call site. Al omitir el modificador in en el sitio de llamada, se indica al compilador que está permitido realizar una copia del argumento por estos motivos:Omitting the in modifier at the call site informs the compiler that it's allowed to make a copy of the argument for the following reasons:

  • Hay una conversión implícita, pero no una conversión de identidad desde el tipo de argumento hacia el tipo de parámetro.There exists an implicit conversion but not an identity conversion from the argument type to the parameter type.
  • El argumento es una expresión, pero no tiene una variable de almacenamiento conocida.The argument is an expression but doesn't have a known storage variable.
  • Existe una sobrecarga que se diferencia por la presencia o la ausencia de in.An overload exists that differs by the presence or absence of in. En ese caso, la sobrecarga por valor es una coincidencia mejor.In that case, the by value overload is a better match.

Estas reglas son útiles cuando se actualiza código existente para usar argumentos de referencia de solo lectura.These rules are useful as you update existing code to use read-only reference arguments. En el método llamado, puede llamar a cualquier método de instancia que use parámetros por valor.Inside the called method, you can call any instance method that uses by value parameters. En esos casos, se crea una copia del parámetro in.In those instances, a copy of the in parameter is created. Dado que el compilador puede crear una variable temporal para cualquier parámetro in, también puede especificar valores predeterminados para cualquier parámetro in.Because the compiler may create a temporary variable for any in parameter, you can also specify default values for any in parameter. En este código se especifica el origen (punto 0,0) como valor predeterminado para el segundo punto:The following code specifies the origin (point 0,0) as the default value for the second point:

private static double CalculateDistance2(in Point3D point1, in Point3D point2 = default)
{
    double xDifference = point1.X - point2.X;
    double yDifference = point1.Y - point2.Y;
    double zDifference = point1.Z - point2.Z;

    return Math.Sqrt(xDifference * xDifference + yDifference * yDifference + zDifference * zDifference);
}

Para forzar al compilador que pase argumentos de solo lectura por referencia, especifique el modificador in en los argumentos en el sitio de llamada, como se muestra en el este código:To force the compiler to pass read-only arguments by reference, specify the in modifier on the arguments at the call site, as shown in the following code:

distance = CalculateDistance(in pt1, in pt2);
distance = CalculateDistance(in pt1, new Point3D());
distance = CalculateDistance(pt1, in Point3D.Origin);

Con este comportamiento es más fácil adoptar parámetros in con el tiempo en grandes bases de código donde es posible mejorar el rendimiento.This behavior makes it easier to adopt in parameters over time in large codebases where performance gains are possible. Primero, puede agregar el modificador in para las firmas de método.You add the in modifier to method signatures first. Después, puede agregar el modificador in en sitios de llamada y crear tipos readonly struct para permitir al compilador que evite la creación de copias defensivas de parámetros in en más ubicaciones.Then, you can add the in modifier at call sites and create readonly struct types to enable the compiler to avoid creating defensive copies of in parameters in more locations.

La designación del parámetro in también se puede usar con tipos de referencia o valores numéricos.The in parameter designation can also be used with reference types or numeric values. Sin embargo, las ventajas de ambos casos son mínimas, si las hay.However, the benefits in both cases are minimal, if any.

No usar nunca valores struct mutables como en el argumento inNever use mutable structs as in in argument

En las técnicas descritas anteriormente se explica cómo evitar copias devolviendo referencias y pasando los valores por referencia.The techniques described above explain how to avoid copies by returning references and passing values by reference. Estas técnicas funcionan mejor cuando los tipos de argumento se declaran como tipos readonly struct.These techniques work best when the argument types are declared as readonly struct types. En caso contrario, el compilador deberá crear copias defensivas en muchas situaciones para aplicar la característica de solo lectura en cualquier argumento.Otherwise, the compiler must create defensive copies in many situations to enforce the readonly-ness of any arguments. Tenga en cuenta el comentario siguiente, en el que se calcula la distancia de un punto 3D respecto del origen:Consider the following example that calculates the distance of a 3D point from the origin:

private static double CalculateDistance(in Point3D point1, in Point3D point2)
{
    double xDifference = point1.X - point2.X;
    double yDifference = point1.Y - point2.Y;
    double zDifference = point1.Z - point2.Z;

    return Math.Sqrt(xDifference * xDifference + yDifference * yDifference + zDifference * zDifference);
}

La estructura Point3D no es un valor struct de solo lectura.The Point3D structure is not a readonly struct. Hay seis llamadas de acceso a propiedades en el cuerpo de este método.There are six different property access calls in the body of this method. En el primer examen, es posible que haya pensado que esos accesos son seguros.On first examination, you may have thought these accesses were safe. Al fin y al cabo, un descriptor de acceso get no debe modificar el estado del objeto.After all, a get accessor shouldn't modify the state of the object. Sin embargo, no hay ninguna regla de lenguaje que lo exija.But there's no language rule that enforces that. Se trata solo de una convención habitual.It's only a common convention. Cualquier tipo podría implementar un descriptor de acceso get que haya modificado el estado interno.Any type could implement a get accessor that modified the internal state. Si el compilador no dispone de ninguna garantía relativa al lenguaje, debe crear una copia temporal del argumento antes de llamar a algún miembro.Without some language guarantee, the compiler must create a temporary copy of the argument before calling any member. El almacenamiento temporal se crea en la pila, los valores del argumento se copian al almacenamiento temporal y el valor se copia en la pila por cada acceso de miembro como argumento this.The temporary storage is created on the stack, the values of the argument are copied to the temporary storage, and the value is copied to the stack for each member access as the this argument. En muchas situaciones, estas copias perjudican tanto el rendimiento que el parámetro de paso por valor es más rápido que el de paso por referencia cuando el tipo de argumento no es un valor readonly struct.In many situations, these copies harm performance enough that pass-by-value is faster than pass-by-readonly-reference when the argument type isn't a readonly struct.

En lugar de ello, si en el cálculo de distancia se usa la estructura inmutable, ReadonlyPoint3D, no se necesitan objetos temporales:Instead, if the distance calculation uses the immutable struct, ReadonlyPoint3D, temporary objects aren't needed:

private static double CalculateDistance3(in ReadonlyPoint3D point1, in ReadonlyPoint3D point2 = default)
{
    double xDifference = point1.X - point2.X;
    double yDifference = point1.Y - point2.Y;
    double zDifference = point1.Z - point2.Z;

    return Math.Sqrt(xDifference * xDifference + yDifference * yDifference + zDifference * zDifference);
}

El compilador genera un código más eficaz cuando se llama a los miembros de un valor readonly struct: La referencia this, en lugar de una copia del receptor, es siempre un parámetro in pasado por referencia al método del miembro.The compiler generates more efficient code when you call members of a readonly struct: The this reference, instead of a copy of the receiver, is always an in parameter passed by reference to the member method. Esta optimización ahorra procesos de copia cuando se utiliza readonly struct como argumento in.This optimization saves copying when you use a readonly struct as an in argument.

No se debe pasar un tipo de valor que admite un valor NULL como argumento in.You shouldn't pass a nullable value type as an in argument. El tipo Nullable<T> no se declara como una estructura de solo lectura.The Nullable<T> type isn't declared as a read-only struct. Eso significa que el compilador debe generar copias defensivas de cualquier argumento de tipo de valor que acepta valores NULL pasado a un método con el modificador in en la declaración de parámetros.That means the compiler must generate defensive copies for any nullable value type argument passed to a method using the in modifier on the parameter declaration.

Puede consultar un programa de ejemplo en el que se muestran las diferencias de rendimiento usando Benchmark.net en el repositorio de muestras de GitHub.You can see an example program that demonstrates the performance differences using Benchmark.net in our samples repository on GitHub. Compara la transmisión de un valor struct mutable por valor y por referencia con la transmisión de un valor struct inmutable por valor y por referencia.It compares passing a mutable struct by value and by reference with passing an immutable struct by value and by reference. El uso de un valor struct inmutable y de la transmisión por referencia es un proceso más rápido.The use of the immutable struct and pass by reference is fastest.

Uso de los tipos ref struct para trabajar con bloques o memoria en un solo marco de pilaUse ref struct types to work with blocks or memory on a single stack frame

Una característica de lenguaje relacionada es la capacidad de declarar un tipo de valor que debe limitarse a un solo marco de pila.A related language feature is the ability to declare a value type that must be constrained to a single stack frame. Esta restricción permite al compilador aplicar una serie de optimizaciones.This restriction enables the compiler to make several optimizations. La principal motivación para esta característica era Span<T> y las estructuras relacionadas.The primary motivation for this feature was Span<T> and related structures. Conseguirá mejoras en el rendimiento a partir de estas optimizaciones usando las API de .NET nuevas y actualizadas que utilizan el tipo Span<T>.You'll achieve performance improvements from these enhancements by using new and updated .NET APIs that make use of the Span<T> type.

Puede tener requisitos similares al trabajar con memoria creada mediante stackalloc o al usar memoria de las API de interoperabilidad.You may have similar requirements working with memory created using stackalloc or when using memory from interop APIs. Puede definir sus propios tipos de ref struct para esas necesidades.You can define your own ref struct types for those needs.

Tipo de readonly ref structreadonly ref struct type

Si declara una estructura como readonly ref se combinan las ventajas y las restricciones de las declaraciones ref struct y readonly struct.Declaring a struct as readonly ref combines the benefits and restrictions of ref struct and readonly struct declarations. La memoria utilizada por el intervalo de solo lectura está limitada a un solo marco de pila y no se puede modificar.The memory used by the readonly span is restricted to a single stack frame, and the memory used by the readonly span can't be modified.

ConclusionesConclusions

El uso de tipos de valor minimiza el número de operaciones de asignación:Using value types minimizes the number of allocation operations:

  • El almacenamiento de los tipos de valor está asignado a la pila en las variables locales y los argumentos de método.Storage for value types is stack allocated for local variables and method arguments.
  • En cuanto al almacenamiento de tipos de valor que son miembros de otros objetos, está asignado como parte del objeto en cuestión, no como una asignación separada.Storage for value types that are members of other objects is allocated as part of that object, not as a separate allocation.
  • Respecto a los valores devueltos por tipo de valor, están asignados a la pila.Storage for value type return values is stack allocated.

Es necesario contrastar eso con los tipos de referencia en estas mismas situaciones:Contrast that with reference types in those same situations:

  • El almacenamiento de los tipos de referencia se asigna al montón en las variables locales y los argumentos de método.Storage for reference types are heap allocated for local variables and method arguments. La referencia se almacena en la pila.The reference is stored on the stack.
  • El almacenamiento de tipos de referencia que son miembros de otros objetos está asignado por separado en la pila.Storage for reference types that are members of other objects are separately allocated on the heap. El objeto contenedor almacena la referencia.The containing object stores the reference.
  • El almacenamiento de los valores devueltos por tipo de referencia está asignado a la pila.Storage for reference type return values is heap allocated. La referencia a ese almacenamiento se guarda en la pila.The reference to that storage is stored on the stack.

La minimización de asignaciones conlleva una serie de renuncias.Minimizing allocations comes with tradeoffs. Se copia más memoria cuando el tamaño del valor struct es mayor que el de una referencia.You copy more memory when the size of the struct is larger than the size of a reference. Habitualmente, las referencias son de 64 o 32 bits y dependen de la CPU de la máquina de destino.A reference is typically 64 bits or 32 bits, and depends on the target machine CPU.

Dichas renuncias suelen tener un impacto mínimo en el rendimiento.These tradeoffs generally have minimal performance impact. Sin embargo, en el caso de los valores struct grandes o de las colecciones de mayor tamaño, el impacto sobre el rendimiento es mayor.However, for large structs or larger collections, the performance impact increases. Dicho impacto puede ser considerable en los bucles estrechos y las rutas de acceso activas de los programas.The impact can be large in tight loops and hot paths for programs.

Estas mejoras del lenguaje C# están diseñadas para algoritmos en los que el rendimiento es fundamental y la minimización de asignaciones de memoria es un factor principal para lograr el rendimiento necesario.These enhancements to the C# language are designed for performance critical algorithms where minimizing memory allocations is a major factor in achieving the necessary performance. Es posible que no use a menudo estas características en el código que escribe.You may find that you don't often use these features in the code you write. Sin embargo, estas mejoras se han adoptado a través de .NET.However, these enhancements have been adopted throughout .NET. Dado que cada vez son más las API que utilizan estas características, verá que el rendimiento de sus aplicaciones aumenta.As more and more APIs make use of these features, you'll see the performance of your applications improve.

Vea tambiénSee also