remarks generate example c# .net design

generate - params comments c#



C#(.NET) Defectos de diseño (30)

¿Cuáles son algunos de los mayores defectos de diseño en C # o .NET Framework en general?

Ejemplo: no hay ningún tipo de cadena no anulable y debe verificar DBNull cuando obtiene valores de un IDataReader.


  1. No soy un gran admirador de las clases Stream, StringWriter, StringReader, TextReader, TextWriter ... simplemente no es intuitivo, qué es qué.
  2. IEnumerable.Reset lanzando una excepción para los iteradores. Tengo algunos componentes de terceros que siempre llaman a restablecer cuando están vinculados a datos, por lo que primero debo enviarlos a una lista para usarlos.
  3. Xml Serializer debería tener elementos IDictionary serializados
  4. Me olvidé por completo de la HttpWebRequest & FTP API qué pena en mi .... (gracias por el comentario Nicholas para recordarme esto :-)

Editar
5. Otra molestia mía es cómo System.Reflection.BindingFlags, tiene diferentes usos dependiendo del método que uses. En FindFields, por ejemplo, ¿qué significa CreateInstance o SetField? Este es un caso donde han sobrecargado el significado detrás de esta enumeración, lo cual es confuso.


  1. There are no subsets of ICollection<T> and IList<T> ; at minimum, a covariant read-only collection interface IListSource<out T> (with an enumerator, indexer and Count) would have been extremely useful.
  2. .NET does not support codeproject.com/Articles/29922/Weak-Events-in-C . The workarounds are clumsy at best, and listener-side workarounds are impossible in partial trust (ReflectionPermission is required).
  3. Generic interface unification is forbidden even when it makes sense and cause no problems.
  4. Unlike in C++, covariant return types are not allowed in .NET
  5. It is not possible to bitwise-compare two value types for equality. In a functional " persistent " data structure, I was writing a Transform(Sequence<T>, Func<T,T>) function that needed to quickly determine whether the function returns the same value or a different value. If the function does not modify most/all of its arguments, then the output sequence can share some/all memory from the input sequence. Without the ability to bitwise compare any value type T, a much slower comparison must be used, which hurts performance tremendously.
  6. .NET doesn''t seem able to support ad-hoc interfaces (like those offered in Go or Rust) in a performant manner. Such interfaces would have allowed you to cast List<T> to a hypothetical IListSource<U> (where T:U) even though the class doesn''t explicitly implement that interface. There are at least three different libraries (written independently) to supply this functionality (with performance drawbacks, of course--if a perfect workaround were possible, it wouldn''t be fair to call it a flaw in .NET).
  7. Other performance issues: IEnumerator requires two interface calls per iteration. Plain method pointers (IntPtr-sized open delegates) or value-typed delegates (IntPtr*2) are not possible. Fixed-size arrays (of arbitrary type T) cannot be embedded inside classes. There is no WeakReference<T> (you can easily write your own, but it will use casts internally.)
  8. The fact that identical delegate types are considered incompatible (no implicit conversion) has been a nuisance for me on some occasions (eg Predicate<T> vs Func<T,bool> ). I often wish we could have structural typing for interfaces and delegates, to achieve looser coupling between components, because in .NET it is not enough for classes in independent DLLs to implement the same interface--they must also share a common reference to a third DLL that defines the interface.
  9. DBNull.Value exists even though null would have served the same purpose equally well.
  10. C# has no ??= operator; you must write variable = variable ?? value . Indeed, there are a few places in C# that needlessly lack symmetry. For example you can write if (x) y(); else z(); (without braces), but you can''t write try y(); finally z(); .
  11. When creating a thread, it is impossible to cause the child thread to inherit thread-local values from the parent thread. Not only does the BCL not support this, but you can''t implement it yourself unless you create all threads manually; even if there were a thread-creation event, .NET can''t tell you the "parents" or "children" of a given thread.
  12. The fact that there are two different length attributes for different data types, "Length" and "Count", is a minor nuisance.
  13. I could go on and on forever about the poor design of WPF ... and WCF (though quite useful for some scenarios) is also full of warts. In general, the bloatedness, unintuitiveness, and limited documentation of many of the BCL''s newer sublibraries makes me reluctant to use them. A lot of the new stuff could have been far simpler, smaller, easier to use and understand, more loosely coupled, better-documented, applicable to more use cases, faster, and/or more strongly typed.
  14. I''m often been bitten by the needless coupling between property getters and setters: In a derived class or derived interface, you can''t simply add a setter when the base class or base interface only has a getter; if you override a getter then you are not allowed to define a setter; and you can''t define the setter as virtual but the getter as non-virtual.

  1. La clase System.Object :

    • Equals y GetHashCode: no todas las clases son comparables o hashable, se deben mover a una interfaz. IEquatable o IComparable (o similar) viene a la mente.

    • ToString: no todas las clases se pueden convertir en una cadena, se deben mover a una interfaz. IFormattable (o similar) viene a la mente.

  2. La propiedad ICollection.SyncRoot :

    • Promueve un diseño deficiente, un bloqueo externo es casi siempre más útil.
  3. Los genéricos deberían haber estado allí desde el principio:

    • El espacio de nombres System.Collections contiene muchas clases e interfaces más o menos obsoletas.


Algunas personas (ISV) desean que se pueda compilar en código de máquina en tiempo de compilación y vincularlo para crear un ejecutable nativo que no necesite el tiempo de ejecución de dotNet.


El comportamiento horrible (y bastante invisible para la mayoría de la gente) O (N ^ 2) de los iterators anidados / recursivos.

Estoy bastante desanimado de que lo sepan, sepan cómo solucionarlo, pero no se considera que tenga la prioridad suficiente como para merecer la inclusión.

Trabajo con estructuras tipo árbol todo el tiempo y tengo que corregir el código de la gente que, de otro modo, sería inteligente, cuando de manera inadvertida introducen operaciones muy caras de esta manera.

La belleza del "rendimiento foreach" es que la sintaxis más simple y fácil fomenta el código correcto y eficaz. Este es el "pozo del éxito" al que creo que deberían aspirar antes de agregar nuevas características para el éxito a largo plazo de la plataforma.


Estoy de acuerdo enfáticamente con esta publicación (para aquellos que no admiten la falta de ToString, existe un atributo de depurador para proporcionar un formato personalizado para su clase).

Además de la lista anterior, también agregaría las siguientes solicitudes razonables:

  1. tipos de referencia no anulables como complemento a tipos de valores que pueden ser anulados,
  2. permitir anular el constructor vacío de una estructura,
  3. permitir restricciones de tipo genérico para especificar clases selladas,
  4. Estoy de acuerdo con otro afiche aquí que solicitó firmas de constructores arbitrarias cuando se usan como restricciones, es decir. donde T: nuevo (cadena), o donde T: nuevo (cadena, int),
  5. También estoy de acuerdo con otro póster aquí sobre la fijación de eventos, tanto para listas de eventos vacías como en la configuración simultánea (aunque esta última es complicada),
  6. los operadores deben definirse como métodos de extensión, y no como métodos estáticos de la clase (o no solo como métodos estáticos al menos),
  7. permitir propiedades y métodos estáticos para las interfaces (Java tiene esto, pero C # no),
  8. permitir la inicialización de eventos en los inicializadores de objetos (solo los campos y las propiedades están actualmente permitidos),
  9. ¿Por qué la sintaxis del "inicializador de objetos" solo se puede utilizar al crear un objeto? ¿Por qué no hacer que esté disponible en cualquier momento, es decir. var e = new Foo (); e {Bar = baz};
  10. arreglar el comportamiento enumerable cuadrático,
  11. todas las colecciones deben tener instantáneas inmutables para la iteración (es decir, mutar la colección no debe invalidar el iterador),
  12. Las tuplas son fáciles de agregar, pero un tipo algebraico cerrado eficiente como "O bien" no lo es, así que me encantaría declarar un tipo algebraico cerrado e imponer una coincidencia de patrones exhaustiva (básicamente soporte de primera clase para el patrón de visitante, pero mucho más eficiente); así que solo tome enumeraciones, extiéndalas con soporte exhaustivo de coincidencia de patrones, y no permita casos inválidos,
  13. Me encantaría el soporte para la coincidencia de patrones en general, pero al menos para la prueba de tipo de objeto; También me gusta la sintaxis de cambio propuesta en otra publicación aquí,
  14. Estoy de acuerdo con otra publicación que las clases de System.IO, como Stream, están algo mal diseñadas; cualquier interfaz que requiera algunas implementaciones para lanzar NotSupportException es un mal diseño,
  15. IList debería ser mucho más simple de lo que es; de hecho, esto puede ser cierto para muchas de las interfaces de colección concretas, como ICollection,
  16. demasiados métodos arrojan excepciones, como IDictionary, por ejemplo,
  17. Preferiría una forma de excepciones comprobadas mejor que la disponible en Java (consulte la investigación sobre los sistemas de tipo y efecto para saber cómo se puede hacer esto),
  18. arreglar varios casos de esquina molestos en resolución de sobrecarga de método genérico; por ejemplo, intente proporcionar dos métodos de extensión sobrecargados, uno que opere en tipos de referencia, y el otro en tipos de estructuras que aceptan valores nulos, y vea cómo le gusta a su inferencia de tipo,
  19. proporcionar una forma de reflejar de forma segura los nombres de campos y miembros para interfaces como INotifyPropertyChanged, que toman el nombre del campo como una cadena; puedes hacer esto usando un método de extensión que toma una lambda con MemberExpression, es decir. () => Foo, pero eso no es muy eficiente,
  20. permitir operadores en interfaces, y hacer que todos los tipos de números core implementen IArithmetic; también son posibles otras interfaces de operador compartidas útiles,
  21. dificultar la mutación de los campos / propiedades del objeto, o al menos, permitir la anotación de campos inmutables y hacer que el verificador de tipos lo haga cumplir (simplemente trátalo como propiedad getter-only para chrissakes, ¡no es difícil!); de hecho, unifica campos y propiedades de una manera más sensata ya que no tiene sentido tener ambos; Las propiedades automáticas de C # 3.0 son un primer paso en esta dirección, pero no van lo suficientemente lejos,
  22. simplificar la declaración de constructores; Me gusta el enfoque de F #, pero la otra publicación aquí que requiere simplemente "nuevo" en lugar del nombre de clase es mejor al menos,

Eso es suficiente por ahora, supongo. Estas son todas las irritaciones que me he encontrado la semana pasada. Probablemente podría continuar durante horas si realmente me lo propongo. C # 4.0 ya está agregando argumentos nombrados, opcionales y predeterminados, lo cual apruebo enfáticamente.

Ahora por una solicitud no razonable:

  1. sería muy, muy agradable si C # / CLR pudiera soportar el polimorfismo constructor de tipo, es decir. genéricos sobre genéricos,

¿Bastante por favor? :-)


Estoy realmente sorprendido de que sea el primero en mencionar este:

Los conjuntos de datos con tipo de ADO.NET no exponen columnas con nulos como propiedades de tipos anulables. Deberías poder escribir esto:

int? i = myRec.Field; myRec.Field = null;

En cambio, tienes que escribir esto, que es simplemente estúpido:

int? i = (int?)myRec.IsFieldNull() ? (int?)null : myRec.Field; myRec.SetFieldNull();

Esto era molesto en .NET 2.0, y es aún más molesto ahora que tienes que usar jiggery-pokery como el anterior en tus lindas consultas LINQ.

También es molesto que el método de la Add<TableName>Row generado sea similar a la noción de tipos anulables. TableAdapter métodos TableAdapter generados no lo son.

No hay mucho en .NET que me haga sentir como si el equipo de desarrollo dijera "Bien, muchachos, estamos lo suficientemente cerca, ¡envíenlo!" Pero esto seguro.


Eventos en C #, donde tienes que verificar explícitamente a los oyentes. ¿No era ese el tema de los eventos, transmitirlo a quien sea que esté allí? Incluso si no hay ninguno?


La forma en que usamos las propiedades me irrita a veces. Me gusta pensar en ellos como el equivalente de los métodos getFoo () y setFoo () de Java. Pero no lo son.

Si las Pautas de uso de la propiedad establecen que las propiedades se pueden configurar en cualquier orden para que la serialización pueda funcionar, entonces son inútiles para la validación del tiempo de establecimiento. Si proviene de un entorno en el que le gusta evitar que un objeto entre en un estado inválido, entonces las propiedades no son su solución. A veces no veo cómo son mejores que los miembros públicos, ya que somos muy limitados en lo que se supone que debemos hacer en las propiedades.

Para ese fin, siempre he deseado (esto es principalmente pensar en voz alta aquí, simplemente me gustaría que pudiera hacer algo como esto) que podría extender la sintaxis de la propiedad de alguna manera. Imagina algo como esto:

private string password; public string Password { // Called when being set by a deserializer or a persistence // framework deserialize { // I could put some backward-compat hacks in here. Like // weak passwords are grandfathered in without blowing up this.password = value; } get { if (Thread.CurrentPrincipal.IsInRole("Administrator")) { return this.password; } else { throw new PermissionException(); } } set { if (MeetsPasswordRequirements(value)) { throw new BlahException(); } this.password = value; } serialize { return this.password; } }

No estoy seguro si eso es útil o cómo sería el acceso a esos. Pero solo desearía poder hacer más con las propiedades y realmente tratarlas como obtener y establecer métodos.


La naturaleza por defecto terriblemente peligrosa de los eventos. El hecho de que puedas convocar un evento y estar en un estado incoherente debido a que los suscriptores se eliminan es simplemente horrible. Vea los excelentes artículos de Jon Skeet y Eric Lippert para leer más sobre el tema.


Los métodos de extensión son agradables, pero son una manera fea de resolver problemas que podrían haberse resuelto más limpiamente con mixins reales (mira ruby ​​para ver de qué estoy hablando), sobre el tema de mixins. Una forma realmente agradable de agregarlos al lenguaje hubiera sido permitir que los genéricos se usen para la herencia. Esto le permite extender las clases existentes de una forma orientada a objetos agradables:

public class MyMixin<T> : T { // etc... }

esto se puede usar así para extender una cadena, por ejemplo:

var newMixin = new MyMixin<string>();

Es mucho más poderoso que los métodos de extensión porque le permite anular los métodos, por ejemplo, para envolverlos y permitir la funcionalidad de tipo AOP dentro del lenguaje.

Perdón por la diatriba :-)


No entiendo que no puedas hacer

donde T: nuevo (U)

Entonces usted declara que el tipo genérico T tiene un constructor no predeterminado.

editar:

Quiero hacer esto:

public class A { public A(string text) { } } public class Gen<T> where T : new(string text) { }


No me gusta la instrucción de cambio de C #.

Me gustaría algo como esto

switch (a) { 1 : do_something; 2 : do_something_else; 3,4 : do_something_different; else : do_something_weird; }

Así que no hay más pausas (fáciles de olvidar) y la posibilidad de separar por comas diferentes valores.


No sé si iría tan lejos como para decir que es un defecto de diseño, pero sería muy bueno si pudieras inferir una expresión lambda de la misma manera que en VB:

VB:

Dim a = Function(x) x * (x - 1)

DO#

Sería bueno si pudiera hacer esto:

var a = x => x * (x - 1);

En lugar de tener que hacer esto:

Func<int, int> a = x => x * (x - 1);

Me doy cuenta de que no es mucho más largo, ¡pero en Code Golf cada personaje cuenta malditamente! ¿No lo toman en cuenta cuando diseñan estos lenguajes de programación? :)


Sabemos mucho sobre las técnicas correctas de OO. Desacoplamiento, programación por contrato, evitando la herencia indebida, el uso apropiado de excepciones, el principio abierto / cerrado, la sustituibilidad de Liskov, y así sucesivamente. Aún así, los marcos .Net no emplean las mejores prácticas.

Para mí, el único defecto más grande en el diseño de .Net no está sobre los hombros de los gigantes; promoviendo paradigmas de programación menos que ideales para las masas de programadores que utilizan sus marcos .

Si MS prestara atención a esto, el mundo de la ingeniería de software podría haber dado grandes saltos en términos de calidad, estabilidad y escalabilidad en esta década, pero lamentablemente, parece estar retrocediendo.


TextWriter es una clase base de StreamWriter. wtf?

Eso siempre me confunde al extremo.


Una de las cosas que me irrita es la paradoja Predicate<T> != Func<T, bool> . Ambos son delegados de tipo T -> bool y, sin embargo, no son compatibles con la asignación.


Una pequeña mascota peev de C #: los constructores usan la sintaxis C ++ / Java de tener el constructor con el mismo nombre que la clase.

New() o ctor() habría sido mucho mejor.

Y, por supuesto, herramientas como Coderush hacen que esto no sea un problema para cambiar el nombre de las clases, pero a partir de un PDV de legibilidad, New () proporciona una gran claridad.



Miembros estáticos y tipos anidados en interfaces.

Esto es particularmente útil cuando un miembro de la interfaz tiene un parámetro de un tipo que es específico de la interfaz ( por ejemplo, una enum ). Sería bueno anidar el tipo de enumeración en el tipo de interfaz.


Don''t like it that you can''t use the values of one enum in another enum, for example:

enum Colors { white, blue, green, red, black, yellow } enum SpecialColors { Colors.blue, Colors.red, Colors.Yellow }


I honestly have to say that during my years of .NET ( C# ) programming I haven''t flaws in the framework design that I''ve remembered; Meaning that in my case there are probably no flaws that are worth remembering.

However, there is something that I dissliked a couple of years back when Microsoft was releasing XNA, they completely cut of their MDX 2.0-version, which made my games unplayable and not easy to just convert. This is a broader flaw and has nothing to do with the .NET-framework.

The .NET-framework actually follows a lot of Very Good design guidelines developed by a lot of the high end language architectures. So I have to say that im happy about .NET.

Pero para decirte algo que podría ser mejor, tendría que quejarme sobre el sistema genérico, no encuentro los genéricos para interfaces como "donde T es MyObj" (esa no es la sintaxis completamente correcta. Sin embargo, esta parte podría haberse hecho mucho mejor y más claro.

Imagina tener una interfaz que comparten dos clases diferentes, si quieres un método genérico dentro de esa interfaz, necesitas revisar algunos desagradables Generics-sytanx. Puede que solo yo quiera hacer cosas raras. Pero solo recuerdo algo memorable para mí.


Microsoft won''t fix obvious bugs in the framework and won''t provide hooks so end users can fix them.

Also, there is no way to binary-patch .NET executables at runtime and no way to specify private versions of .NET framework libraries without binary patching the native libraries (to intercept the load call), and ILDASM is not redistributable so I cannot automate the patch anyway.


One thing that ticked me off in 1.x was when using the System.Xml.XmlValidatingReader , the ValidationEventHandler ''s ValidationEventArgs doesn''t expose the underlying XmlSchemaException (marked internal) which has all the useful info like linenumber and position . Instead you''re expected to parse this out of the Message string property or use reflection to dig it out. Not so good when you want to return a more sanitised error to the end user.


The .Parameters.Add() method on the SqlCommand in V1 of the framework was horribly designed -- one of the overloads would basically not work if you passed in a parameter with a value (int) of 0 -- this led to them creating the .Parameters.AddWithValue() method on the SqlCommand class.


To add to the long list of good points made by others already:

  • DateTime.Now == DateTime.Now in most, but not all cases.

  • String which is immutable has a bunch of options for construction and manipulation, but StringBuilder (which is mutable) doesn''t.

  • Monitor.Enter and Monitor.Exit should have been instance methods, so instead of newing a specific object for locking, you could new a Monitor and lock on that.

  • Destructors should never have been named destructors. The ECMA spec calls them finalizers, which is much less confusing for the C++ crowd, but the language specification still refers to them as destructors.


  • el método Reset() en IEnumerator<T> fue un error (para bloques de iterador, la especificación del lenguaje incluso exige que esto arroje una excepción)
  • los métodos de reflexión que devuelven matrices eran, en opinión de Eric, un error
  • la covarianza de matriz era y sigue siendo una rareza; al menos en C # 4.0 / .NET 4.0 esto se hace correctamente para IEnumerable[<T>]
  • ApplicationException cayó en desgracia, ¿fue un error?
  • Colecciones sincronizadas: una buena idea, pero no necesariamente útil en la realidad: generalmente necesita sincronizar varias operaciones ( Contains , luego Add ), por lo que una colección que sincroniza distintas operaciones no es tan útil.
  • se podría haber hecho más uso del patrón de using / lock , quizás permitiéndoles compartir una sintaxis reutilizable (¿extensible?); puedes simular esto volviendo IDisposable y usando using , pero podría haber sido más claro
  • bloques de iteradores: no hay una forma simple de verificar los argumentos antes de tiempo (en lugar de hacerlo de forma perezosa). Claro, puedes escribir dos métodos encadenados, pero eso es feo
  • una inmutabilidad más simple sería agradable; C # 4.0 ayuda un poco , pero no lo suficiente
  • no hay soporte para "este parámetro de tipo ref no puede ser nulo", aunque los contratos (en 4.0) ayudan con esto. Pero la sintaxis como Foo(SqlConnection! connection) (Que inyecta un null-check / throw ) sería agradable (contraste con int? Etc)
  • falta de soporte de operadores y constructores no predeterminados con genéricos; C # 4.0 resuelve esto un poco con dynamic , o puedes habilitarlo así
  • la variable del iterador se declara fuera del tiempo en la expansión foreach , lo que significa que anon-methods / lambdas captura la variable individual, en lugar de uno por iteración (doloroso con el enhebrado / asincronismo / etc.)

  • null todas partes.

  • const ninguna parte.

  • Las API son inconsistentes, por ejemplo, la mutación de una matriz devuelve el void pero al agregar a StringBuffer devuelve la misma mutable StringBuffer .

  • Las interfaces de recopilación son incompatibles con estructuras de datos inmutables, por ejemplo Add in System.Collections.Generic.IList<_> no puede devolver un resultado.

  • No hay tipado estructural, por lo que debe escribir System.Windows.Media.Effects.SamplingMode.Bilinear lugar de solo Bilinear .

  • Interfaz IEnumerator implementada por clases cuando debería ser una struct inmutable.

  • La igualdad y la comparación son un desastre: tienes System.IComparable e Equals pero también tienes System.IComparable<_> , System.IEquatable , System.Collections.IComparer , System.Collections.IStructuralComparable , System.Collections.IStructuralEquatable , System.Collections.Generic.IComparer y System.Collections.Generic.IEqualityComparer .

  • Las tuplas deben ser estructuras pero las estructuras inhiben innecesariamente la eliminación de llamadas de cola, por lo que uno de los tipos de datos más comunes y fundamentales se asignará innecesariamente y destruirá el paralelismo escalable.


  • Be able to invoke an extension method on null variable is arguable eg

    object a=null; a.MyExtMethod(); // this is callable, assume somewhere it has defined MyExtMethod

    It could be handy but it is ambiguous on null reference exception topics.

  • One naming ''flaw''. ''C'' of "configuration" in System.configuration.dll should be capitalized.

  • Exception handling. Exception should be forcibly caught or thrown like in Java, the compiler should check it at compilation time. Users should not rely on comments for exceptions info within the target invocation.