Code Wars II - Array Helpers - Extension Methods

in STEMGeeks3 months ago (edited)

Kata: Array Helpers - 6 kyu

Situation: This kata is designed to test your ability to extend the functionality of built-in classes. In this case, we want you to extend the built-in Array class with the following methods: Square(), Cube(), Average(), Sum(), Even() and Odd().

Explanation:

  • Square() must return a copy of the array, containing all values squared
  • Cube() must return a copy of the array, containing all values cubed
  • Average() must return the average of all array values; on an empty array must return NaN
  • Sum() must return the sum of all array values
  • Even() must return an array of all even numbers
  • Odd() must return an array of all odd numbers

Note: the original array must not be changed in any case!

Situación: Este kata está diseñado para probar tu habilidad para extender la funcionalidad de las clases incorporadas. En este caso, queremos que extiendas la clase incorporada Array con los siguientes métodos: Square(), Cube(), Average(), Sum(), Even() y Odd().

Explicación:

  • Square() debe devolver una copia de la matriz, que contiene todos los valores al cuadrado
  • Cube() debe devolver una copia de la matriz, que contiene todos los valores al cubo
  • Average() debe devolver la media de todos los valores de la matriz; en una matriz vacía debe devolver NaN
  • Sum() debe devolver la suma de todos los valores de la matriz
  • Even() debe devolver una matriz con todos los números pares
  • Odd() debe devolver una matriz con todos los números impares

Nota: la matriz original no debe ser cambiada bajo ninguna circunstancia!

Example:

Captura de pantalla 2024-07-24 110733.png

The exercise itself has no difficulty. Its rating of 6 kyu, in my opinion, refers to the knowledge of the concept it addresses: “extending class functionalities” and is therefore worthwhile to explain the use of the this keyword and extension methods.

El ejercicio en sí no tiene ninguna dificultad. Su calificación de 6 kyu, en mi opinión, se refiere al conocimiento del concepto que aborda: “extender funcionalidades de clases” y, por tanto, vale la pena para explicar el uso de la palabra reservada this y los métodos de extensión.


Extension methods [Métodos de extensión]

Extension methods enable you to "add" methods to existing types (class, struct or interface) without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are static methods, but they're called as if they were instance methods on the extended type. For client code written in C# there's no apparent difference between calling an extension method and the methods defined in a type. They are declared by placing the this keyword in front of the first parameter of the method. The data type of the parameter indicates which type is going to be extended. Note that this parameter is not included in the call (if the call is made from the extended type). The method must be declared in a non-nested and non-generic class, although the method itself can be perfectly generic. Extension methods are only in scope when you explicitly import the namespace into your source code with a using directive.

Los métodos de extensión permiten “agregar” métodos a los tipos existentes (class, struct o interface) sin tener que crear un nuevo tipo que herede del actual, volver a compilar o modificar el tipo original. Los métodos de extensión se definen como métodos estáticos, pero se llaman mediante la sintaxis del método de instancia. En el caso del código de cliente escrito en C#, no hay ninguna diferencia aparente entre llamar a un método de extensión y los métodos definidos en un tipo. Se declaran colocando la palabra reservada this delante del primer parámetro del método. El tipo de dato del parámetro indica cual es el tipo que va a ser extendido. Ten en cuenta que este parámetro no se incluye en la llamada (si la llamada se hace desde el tipo extendido). El método debe ser declarado en una clase no anidada y no genérica, aunque el método en sí puede ser perfectamente genérico. Los métodos de extensión sólo están en el ámbito de aplicación cuando se importa explícitamente el espacio de nombres en el código fuente con una directiva using.


The intermediate language (IL) generated by the compiler translates your code into a call on the static method. The principle of encapsulation isn't really being violated, since extension methods can't access private variables in the type they're extending.

El lenguaje intermedio (IL) generado por el compilador traduce el código en una llamada al método estático. En ningún momento se viola el principio de encapsulación, pues los métodos de extensión no pueden acceder a variables privadas del tipo que están extendiendo.



Both the MyExtensions class and the WordCount() method are static, and it can be accessed like all other static members. The WordCount() method can be invoked like other static methods as follows:

Tanto la clase MyExtensions como el método WordCount() son estáticos, y se puede acceder a ellos como a todos los demás miembros estáticos. El método WordCount() puede ser invocado como otros métodos estáticos de la siguiente manera:



You can use extension methods to extend a class or interface, but not to override them. An extension method with the same name and signature as an interface or class method will never be called. At compile time, extension methods always have lower priority than instance methods defined in the type itself. In other words, if a type has a method named Process(int i), and you have an extension method with the same signature, the compiler will always bind to the instance method. When the compiler encounters a method invocation, it first looks for a match in the type's instance methods. If no match is found, it searches for any extension methods that are defined for the type, and bind to the first extension method that it finds.
The following code demonstrates what was expressed in the previous paragraph. The static class Extension contains extension methods defined for any type that implements IMyInterface. Classes A, B, and C all implement the interface. The MethodB extension method is never called because its name and signature exactly match methods already implemented by the classes. When the compiler can't find an instance method with a matching signature, it will bind to a matching extension method if one exists.

Los métodos de extensión se pueden usar para extender una clase o interfaz, pero no para sobrescribirlas. Un método de extensión con el mismo nombre y firma que un método de clase o interfaz nunca será invocado. En tiempo de compilación los métodos de extensión siempre tienen menor prioridad que los métodos de instancia que ya estaban definidos en el propio tipo. En otras palabras, si un tipo tiene un método llamado Process(int i), y tienes un método de extensión con la misma firma, el compilador siempre dará prioridad al método de instancia. Cuando el compilador encuentra una invocación a un método, primero busca una coincidencia en los métodos de instancia del tipo. Si no encuentra ninguna coincidencia, busca cualquier método de extensión que esté definido, y se enlaza al primer método de extensión que encuentre con la misma firma.
El siguiente código demuestra lo expresado en el párrafo anterior. La clase estática Extension contiene métodos de extensión definidos para cualquier tipo que implemente IMyInterface. Las clases A, B y C implementan la interfaz. El método de extensión MethodB() nunca se llama porque su nombre y firma coinciden exactamente con los métodos ya implementados por las clases. Cuando el compilador no puede encontrar un método de instancia con una firma coincidente, se enlazará a un método de extensión coincidente si existe uno.






Extending predefined types can be difficult with struct types because they're passed by value to methods. That means any changes to the struct are made to a copy of the struct. Those changes aren't visible once the extension method exits. You can add the ref modifier to the first argument making it a ref extension method. The ref keyword can appear before or after the this keyword without any semantic differences.
Since adding the ref modifier indicates that the first argument is passed by reference, this enables you to write extension methods that change the state of the struct being extended. Only value types or generic types constrained to struct are allowed as the first parameter of a ref extension method. The following example shows how to use a ref extension method to directly modify a built-in type without the need to reassign the result or pass it through a function with the ref keyword:

Extender tipos predefinidos puede ser difícil cuando se trata con struct, ya que se pasan por valor a los métodos. Esto significa que cualquier cambio en el struct se realiza en una copia del struct. Esos cambios no son visibles una vez que la ejecución del método termina. Puedes añadir el modificador ref al primer argumento, convirtiéndolo en un método de extensión que funciona por referencia. La palabra clave ref puede aparecer antes o después de la palabra clave this sin que exista ninguna diferencia semántica.

Ya que añadir el modificador ref indica que el primer argumento se pasa por referencia, esto permite escribir métodos de extensión que cambian el estado del struct que se está extendiendo. Sólo se permiten como primer parámetro de un método de extensión ref los tipos de valor o los tipos genéricos. El siguiente ejemplo muestra cómo utilizar un método de extensión ref para modificar directamente un tipo incorporado sin necesidad de reasignar el resultado o pasarlo a través de una función con la palabra clave ref:




While it's still considered preferable to add functionality by modifying an object's code or deriving a new type whenever it's reasonable and possible to do so, extension methods have become a crucial option for creating reusable functionality in C# and throughout the .NET ecosystem. When using an extension method to extend a type whose source code you aren't in control of, you run the risk that a change in the implementation of the type will cause your extension method to break. For those occasions when the original source isn't under your control, when a derived object is inappropriate or impossible, or when the functionality shouldn't be exposed beyond its applicable scope, extension methods are an excellent choice. For example, if we try to inherit from the Array class, the following error message will be thrown: Extensions cannot be derived from the special Array class (CS0644). If we click on the link it will take us to Microsoft Learn, the official C# and .NET documentation, and we will see the compilation error CS0644: Classes cannot explicitly inherit from any of the following base classes:

  • System.Enum
  • System.ValueType
  • System.Delegate
  • System.Array

The compiler uses them as implicit base classes. For example, System.ValueType is the implicit base class for struct.

Aunque todavía se considera preferible añadir funcionalidad modificando el código de un objeto o creando un nuevo tipo que herede de otro ya existente, siempre que sea razonable y posible hacerlo, los métodos de extensión son una opción crucial para crear funcionalidad reutilizable en C# y todo el ecosistema .NET en general. Cuando se utiliza un método de extensión para extender un tipo cuyo código fuente no controlas, se corre el riesgo de que un cambio en la implementación del tipo provoque la rotura del método de extensión. Para aquellas ocasiones en las que la fuente original no está bajo tu control directo, cuando la funcionalidad no debe exponerse más allá de su ámbito aplicable, o cuando es inapropiado o imposible derivar de otro tipo, los métodos de extensión son una excelente opción. Por ejemplo, si intentamos heredar de la clase Array, se lanzará el siguiente mensaje de error: Extensions no se puede derivar de la clase especial Array (CS0644). Si pinchamos el enlace nos llevará a Microsoft Learn, la documentación oficial de C# y .NET, y veremos el error de compilación CS0644: Las clases no pueden heredar explícitamente de ninguna de las siguientes clases base:

  • System.Enum
  • System.ValueType
  • System.Delegate
  • System.Array

El compilador las utiliza como clases base implícitas. Por ejemplo, System.ValueType es la clase base implícita de los struct.



So it's all for today's blog. In the next one I will be giving a solution to the exercise. Click here to go.

Esto es todo por ahora. En el próximo blog estaré dándole solución al ejercicio. Pincha aquí para ir.


Bibliography [Bibliografía]

Extension Methods - C# | Microsoft Learn
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods

Compile Error CS0644 | Microsoft Learn
https://learn.microsoft.com/en-us/dotnet/csharp/misc/cs0644?f1url=%3FappId%3Droslyn%26k%3Dk

C# in Depth 4th edition - Jon Skeet



English translation made with DeepL Translate
Traducción al Inglés hecha con Deepl Translate