una qué que programacion objetos objeto metodos metodo mandar llamar herencia ejemplos como clases clase c# arrays design initialization

c# - qué - que es una clase en programacion



Inicialización elegante de una matriz de instancias de clase en C# (3)

La manera más sucinta en que puedo pensar para su ejemplo particular implica escribir un operador implícito para la clase Fraction:

public sealed class Fraction { public Fraction(int n, int d) { Numerator = n; Deniminator = d; } public int Numerator { get; } public int Deniminator { get; } public static implicit operator Fraction(int[] data) { return new Fraction(data[0], data[1]); } }

Entonces puedes inicializarlo así:

var fractions = new Fraction[] { new [] {1, 2}, new [] {3, 4}, new [] {5, 6} };

Lamentablemente, todavía necesita el new [] en cada línea, por lo que no creo que esto gane mucho con la sintaxis de inicialización de la matriz normal:

var fractions = new [] { new Fraction(1, 2), new Fraction(3, 4), new Fraction(5, 6) };

Supongo que podría escribir un Func<> "local" con un nombre corto para simplificar algo la inicialización:

Func<int, int, Fraction> f = (x, y) => new Fraction(x, y); var fractions = new [] { f(1, 2), f(3, 4), f(5, 6) };

El inconveniente es que necesitarías agregar esa línea adicional (inicializando un Func<> ) donde quisieras inicializar el arreglo - o tener un método estático privado en la clase en su lugar - pero luego ese método estaría dentro del alcance de toda la clase , que no es ideal si tiene un nombre de una sola letra.

Sin embargo, la ventaja de este enfoque es que es muy flexible.

Jugué con la idea de llamar a la función en línea _ , pero realmente no estoy seguro de eso ...

Func<int, int, Fraction> _ = (x, y) => new Fraction(x, y); var fractions = new [] { _(1, 2), _(3, 4), _(5, 6) };

Digamos que tengo una clase como esta:

public class Fraction { int numerator; int denominator; public Fraction(int n, int d) { // set the member variables } // And then a bunch of other methods }

Quiero inicializar una matriz de ellos de una manera agradable, y esta publicación es una gran lista de enfoques que son propensos a errores o sintácticamente engorrosos.

Por supuesto, un constructor de arreglos sería bueno, pero no existe tal cosa:

public Fraction[](params int[] numbers)

Entonces me veo obligado a usar un método como

public static Fraction[] CreateArray(params int[] numbers) { // Make an array and pull pairs of numbers for constructor calls }

que es relativamente torpe, pero no veo una forma de evitarlo.

Ambas formas son propensas a errores porque un usuario podría pasar erróneamente un número impar de parámetros, tal vez porque se salteó un valor, lo que dejaría a la función rascándose la cabeza preguntándose qué es lo que realmente quería el usuario. Podría arrojar una excepción, pero luego el usuario tendría que intentar / atrapar. Prefiero no imponer eso al usuario si es posible. Entonces hagamos cumplir los pares.

public static Fraction[] CreateArray(params int[2][] pairs)

Pero no se puede llamar a este CreateArray de una manera agradable, como

Fraction.CreateArray({0,1}, {1,2}, {1,3}, {1,7}, {1,42});

Ni siquiera puedes hacer

public static Fraction[] CreateArray(int[2][] pairs) // Then later... int[2][] = {{0,1}, {1,2}, {1,3}, {1,7}, {1,42}}; Fraction.CreateArray(numDenArray);

Tenga en cuenta que esto funcionaría perfectamente en C ++ (estoy bastante seguro).

En su lugar, estás obligado a hacer uno de los siguientes, lo cual es aborrecible. La sintaxis es terrible y parece realmente incómodo utilizar una matriz dentada cuando todos los elementos tienen la misma longitud.

int[2][] fracArray = {new int[2]{0,1}, /*etc*/); Fraction.CreateArray(fracArray); // OR Fraction.CreateArray(new int[2]{0,1}, /*etc*/);

Del mismo modo, las tuplas estilo Python son ilegales y la versión C # es asquerosa:

Fraction.CreateArray(new Tuple<int,int>(0,1), /*etc*/);

El uso de una matriz 2D pura puede tomar la siguiente forma, pero es ilegal, y estoy seguro de que no hay forma legal de expresarlo:

public static Fraction[] CreateArray(int[2,] twoByXArray) // Then later... Fraction[] fracArray = Fraction.CreateArray(new int[2,4]{{0,1}, {1,2}, {1,3}, {1,6}});

Esto no aplica pares:

public static Fraction[] CreateArray(int[,] twoByXArray)

OK, ¿qué tal

public static Fraction[] CreateArray(int[] numerators, int[] denominators)

Pero entonces las dos matrices pueden tener diferentes longitudes. C ++ permite

public static Fraction[] CreateArray<int N>(int[N] numerators, int[N] denominators)

pero, bueno, esto no es C ++, ¿o sí?

Este tipo de cosas es ilegal:

public static implicit operator Fraction[](params int[2][] pairs)

y no funcionable de todos modos, nuevamente debido a la sintaxis aborrecible:

Fraction[] fracArray = new Fraction[](new int[2]{0,1}, /*etc*/ );

Esto podría ser bueno:

public static implicit operator Fraction(string s) { // Parse the string into numerator and denominator with // delimiter ''/'' }

Entonces puedes hacer

string[] fracStrings = new string[] {"0/1", /*etc*/}; Fraction[] fracArray = new Fraction[fracStrings.Length]; int index = 0; foreach (string fracString in fracStrings) { fracArray[index] = fracStrings[index]; }

No me gusta este enfoque por cinco razones. Uno, el lanzamiento implícito crea una instancia inevitable de un nuevo objeto, pero ya tenemos uno perfectamente bueno, es decir, el que estamos tratando de inicializar. Dos, puede ser confuso para leer. Tres, te obliga a hacer explícitamente lo que quería encapsular en primer lugar. Cuatro, deja espacio para el mal formato. Cinco, implica un análisis sintáctico de cadenas literales, que es más como una broma práctica que un buen estilo de programación.

Lo siguiente también requiere una instanciación derrochadora:

var fracArray = Array.ConvertAll(numDenArray, item => (Fraction)item);

El siguiente uso de una propiedad tiene el mismo problema a menos que use esas terribles matrices irregulares:

public int[2] pair { set { numerator = value[0]; denominator = value[1]; } } // Then later... var fracStrings = new int[2,4] {{0,1}, /*etc*/}; var fracArray = new Fraction[fracStrings.Length]; int index = 0; foreach (int[2,] fracString in fracStrings) { fracArray[index].pair = fracStrings[index]; }

Esta variación no aplica pares:

foreach (int[,] fracString in fracStrings) { fracArray[index].pair = fracStrings[index]; }

De nuevo, este enfoque es grande de todos modos.

Estas son todas las ideas que sé cómo derivar. ¿Hay una buena solución?


No puedo pensar en una solución elegante y al mismo tiempo eficiente en cuanto a la memoria para array.

Pero hay una solución elegante para la lista (y similar) que utiliza la función de inicialización de la colección C # 6:

public static class Extensions { public static void Add(this ICollection<Fraction> target, int numerator, int denominator) { target.Add(new Fraction(numerator, denominator)); } }

Con ese método de extensión en su lugar, puede inicializar fácilmente una lista de Fraction por ejemplo:

var list = new List<Fraction> { { 0, 1 }, { 1, 2 }, { 1, 3 }, { 1, 7 }, { 1, 42 } };

Y, por supuesto, aunque no es eficiente desde el punto de vista de la memoria, puede usarlo para inicializar el conjunto de Fraction :

var array = new List<Fraction> { { 0, 1 }, { 1, 2 }, { 1, 3 }, { 1, 7 }, { 1, 42 } }.ToArray();

o incluso haciéndolo más conciso al declarar una clase derivada de lista con un operador de conversión de matriz implícita:

public class FractionList : List<Fraction> { public static implicit operator Fraction[](FractionList x) => x?.ToArray(); }

y luego usa

Fraction[] array = new FractionList { { 0, 1 }, { 1, 2 }, { 1, 3 }, { 1, 7 }, { 1, 42 } };


Puede crear un constructor de matriz de fracciones con una interfaz fluida. Llevaría a algo así como

public class FractionArrayBuilder { private readonly List<Fraction> _fractions = new List<Fraction>(); public FractionArrayBuilder Add(int n, int d) { _fractions.Add(new Fraction(n, d)); return this; } public Fraction[] Build() { return _fractions.ToArray(); } }

que se puede llamar usando

var fractionArray = new FractionArrayBuilder() .Add(1,2) .Add(3,4) .Add(3,1) .Build();

que es una declaración fácil de entender.

He hecho un fiddle para demostrar.