remarks example cref c# data-structures struct anonymous-types

example - remarks c#



Alternativa no solo de lectura a tipos anónimos (5)

En C #, un tipo anónimo puede ser el siguiente:

method doStuff(){ var myVar = new { a = false, b = true } if (myVar.a) { // Do stuff } }

Sin embargo, lo siguiente no se compilará:

method doStuff(){ var myVar = new { a = false, b = true } if (myVar.a) { myVar.b = true; } }

Esto se debe a que los campos de myVar son de solo lectura y no se pueden asignar. Parece querer hacer algo como que el último es bastante común; quizás la mejor solución que he visto es simplemente definir una estructura fuera del método.

Sin embargo, ¿realmente no hay otra manera de hacer que funcione el bloque anterior? La razón por la que me molesta es que myVar es una variable local de este campo, por lo que parece que solo debería referirse al método que lo utiliza. Además, la necesidad de colocar la estructura fuera del método puede hacer que la declaración de un objeto esté bastante lejos de su uso, especialmente en un método largo.

Dicho de otra manera, ¿existe una alternativa a los tipos anónimos que me permita definir una "estructura" como esta (me doy cuenta de que struct existe en C # y debe definirse fuera de un método) sin hacerlo de solo lectura? Si no, ¿hay algo fundamentalmente malo en querer hacer esto, y debería estar usando un enfoque diferente?


¿Existe una alternativa a los tipos anónimos que me permita definir de forma concisa un tipo de "registro" simple como este sin hacerlo de solo lectura?

No. Tendrás que hacer un tipo nominal.

Si no, ¿hay algo fundamentalmente malo en querer hacer esto?

No, es una característica razonable que hemos considerado antes.

Observo que en Visual Basic, los tipos anónimos son mutables si quieres que lo sean.

Lo único realmente "fundamentalmente erróneo" sobre un tipo anónimo mutable es que sería peligroso usar uno como clave hash. Diseñamos tipos anónimos con la suposición de que (1) los usará como claves en equijoins en las consultas de consultas LINQ, y (2) en LINQ-to-Objects y otras implementaciones, las uniones se implementarán usando tablas hash. Por lo tanto, los tipos anónimos deberían ser útiles como claves hash, y las claves hash mutables son peligrosas.

En Visual Basic, la implementación de GetHashCode no consume ninguna información de campos mutables de tipos anónimos. Aunque ese es un compromiso razonable, simplemente decidimos que en C # la complejidad adicional no valía la pena.


En C # 7 podemos aprovechar tuplas con nombre para hacer el truco:

(bool a, bool b) myVar = (false, true); if (myVar.a) { myVar.b = true; }


No podrá obtener la buena sintaxis de inicialización, pero la clase ExpandoObject presentada en .NET 4 podría servir como una solución viable.

dynamic eo = new ExpandoObject(); eo.SomeIntValue = 5; eo.SomeIntValue = 10; // works fine


No, tendrás que crear tu propia clase o estructura para hacer esto (preferiblemente una clase si quieres que sea mutable, las estructuras mutables son horribles).

Si no te importan las implementaciones Equals / ToString / GetHashCode , es bastante fácil:

public class MyClass { public bool Foo { get; set; } public bool Bar { get; set; } }

(Todavía usaría propiedades en lugar de campos, por varias razones ).

Personalmente, suelo encontrar un tipo inmutable que pueda pasar de un método a otro, quiero una versión con nombre de la función de tipo anónimo existente ...


Para los tipos de operación anteriores, debe definir su propio STRUCT mutable. Las estructuras mutables pueden ser un dolor de cabeza para escritores de compilación como Eric Lippert, y hay algunas limitaciones desafortunadas en cómo .net las maneja, pero no obstante la semántica de las estructuras mutables "Plain Old Data" (estructuras en las que todos los campos son públicos, y el único las funciones públicas que escriben this son constructores, o son llamadas exclusivamente por los constructores) ofrecen una semántica mucho más clara de lo que se puede lograr a través de las clases.

Por ejemplo, considere lo siguiente:

struct Foo { public int bar; ...other stuff; } int test(Action<Foo[]> proc1, Action<Foo> proc2) { foo myFoos[] = new Foo[100]; proc1(myFoos); myFoos[4].bar = 9; proc2(myFoos[4]); // Pass-by-value return myFoos[4].bar; }

Suponiendo que no hay un código inseguro y que los delegados que se han transferido pueden ser llamados y regresarán en un tiempo determinado, ¿qué devolverá la test() ? El hecho de que Foo sea ​​una estructura con una bar campo pública es suficiente para responder la pregunta: devolverá 9, independientemente de lo que aparezca en la declaración de Foo , y sin importar qué funciones se pasen en proc1 y proc2 . Si Foo fuera una clase, habría que examinar cada Action<Foo[]> y Action<Foo> que existe, o que existirá, para saber qué devolvería la test() . Determinar que Foo es una estructura con bar campo pública parece mucho más fácil que examinar todas las funciones pasadas y futuras que podrían pasarse.

Los métodos Struct que modifican this se manejan particularmente mal en .net, así que si uno necesita usar un método para modificar una estructura, es casi seguro que es mejor usar uno de estos patrones:

myStruct = myStruct.ModifiedInSomeFashion(...); // Approach #1 myStructType.ModifyInSomeFashion(ref myStruct, ...); // Approach #2

que el patrón:

myStruct.ModifyInSomeFashion(...);

Siempre que uno use el enfoque anterior para modificar patrones de estructura, sin embargo, las estructuras mutables tienen la ventaja de permitir código que es más eficiente y más fácil de leer que las estructuras inmutables o inmutables, y es mucho menos propenso a problemas que las clases mutables. Para las cosas que representan una agregación de valores, sin identidad fuera de los valores que contienen, los tipos de clases mutables a menudo son la peor representación posible.