Instrucciones de diseño para programadores de ... - Willy .Net
Instrucciones de diseño para programadores de ... - Willy .Net
Instrucciones de diseño para programadores de ... - Willy .Net
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
<strong>Instrucciones</strong> <strong>de</strong> diseño <strong>para</strong> <strong>programadores</strong> <strong>de</strong>bibliotecas <strong>de</strong> clasesEl entorno administrado <strong>de</strong> .NET Framework permite a los <strong>programadores</strong> mejorar el mo<strong>de</strong>lo <strong>de</strong>programación <strong>para</strong> hacerlo compatible con una amplia gama <strong>de</strong> funcionalida<strong>de</strong>s. Las instrucciones<strong>de</strong> diseño <strong>de</strong> .NET Framework tienen como finalidad fomentar la coherencia y la previsibilidad en lasAPI públicas al habilitar la integración entre lenguajes y el Web. Es muy importante seguir estasinstrucciones <strong>de</strong> diseño cuando se programan las clases y los componentes que extien<strong>de</strong>n .NETFramework. El diseño incoherente influye <strong>de</strong> un modo <strong>de</strong>sfavorable en la productividad <strong>de</strong> los<strong>programadores</strong>. Los complementos y las herramientas <strong>de</strong> <strong>de</strong>sarrollo pue<strong>de</strong>n convertir algunas <strong>de</strong>estas instrucciones en reglas preceptivas <strong>de</strong> hecho y reducir el valor <strong>de</strong> los componentes que no lascumplen. Los componentes que no se ajustan a estas instrucciones <strong>de</strong> diseño funcionarán, aunqueno lo harán con todo su potencial.Con estas instrucciones se preten<strong>de</strong> ayudar a los diseñadores <strong>de</strong> bibliotecas <strong>de</strong> clases acompren<strong>de</strong>r las ventajas y las <strong>de</strong>sventajas entre las distintas soluciones. Pue<strong>de</strong> que hayasituaciones en las que un buen diseño <strong>de</strong> bibliotecas requiera saltarse estas instrucciones <strong>de</strong>diseño. Estos casos no son lo habitual y es importante que una <strong>de</strong>cisión <strong>de</strong> este tipo estéa<strong>de</strong>cuadamente justificada. En esta sección se proporcionan instrucciones <strong>de</strong> uso y <strong>de</strong>nomenclatura <strong>para</strong> los tipos <strong>de</strong> .NET Framework, así como instrucciones <strong>de</strong> implementación <strong>de</strong>mo<strong>de</strong>los <strong>de</strong> diseño comunes.
Relación con el sistema <strong>de</strong> tipos común y conCommon Language SpecificationEl Sistema <strong>de</strong> tipos común es el mo<strong>de</strong>lo que <strong>de</strong>fine las reglas que se siguen en Common LanguageRuntime <strong>para</strong> <strong>de</strong>clarar, utilizar y administrar tipos. Este sistema establece un marco <strong>de</strong> trabajo quepermite la integración entre lenguajes, la seguridad <strong>de</strong> tipos y ejecutar código <strong>de</strong> alto rendimiento.Es la materia prima a partir <strong>de</strong> la cual se pue<strong>de</strong>n crear bibliotecas <strong>de</strong> clases.Common Language Specification (CLS) <strong>de</strong>fine un conjunto <strong>de</strong> reglas comprobables medianteprogramación que <strong>de</strong>termina la interoperación <strong>de</strong> los tipos creados en distintos lenguajes <strong>de</strong>programación. La selección <strong>de</strong> CLS es un modo excelente <strong>de</strong> garantizar la interoperación entrelenguajes. Los diseñadores <strong>de</strong> bibliotecas <strong>de</strong> clases administradas pue<strong>de</strong>n utilizar CLS <strong>para</strong>asegurarse <strong>de</strong> que las API creadas se pue<strong>de</strong>n llamar <strong>de</strong>s<strong>de</strong> una amplia gama <strong>de</strong> lenguajes <strong>de</strong>programación. Tenga en cuenta que aunque CLS promueve el diseño correcto <strong>de</strong> bibliotecas, no loimpone. Para obtener más información sobre este tema, vea Escribir código compatible con CLS.Para <strong>de</strong>terminar las funciones que se <strong>de</strong>ben incluir en una biblioteca <strong>de</strong> clases, siga estos dosprincipios orientativos con relación a CLS:1. Determinar si la función facilita el tipo <strong>de</strong> <strong>de</strong>sarrollo API a<strong>de</strong>cuado al espacio administrado.CLS <strong>de</strong>be ser lo suficientemente completo como <strong>para</strong> permitir escribir cualquier tipo <strong>de</strong>biblioteca administrada. No obstante, si proporciona múltiples formas <strong>de</strong> realizar una mismatarea, pue<strong>de</strong> <strong>de</strong>sorientar al usuario <strong>de</strong> la biblioteca <strong>de</strong> clases a la hora <strong>de</strong> <strong>de</strong>cidir sobre lautilización y el diseño correctos. Por ejemplo, si incluye construcciones seguras yconstrucciones no seguras los usuarios <strong>de</strong>berán elegir entre estas dos opciones. Porconsiguiente, CLS fomenta el uso correcto porque ofrece sólo construcciones con seguridad <strong>de</strong>tipos.2. Determinar si <strong>para</strong> el compilador pue<strong>de</strong> ser difícil exponer la función.Todos los lenguajes <strong>de</strong> programación requerirán algún tipo <strong>de</strong> modificación <strong>para</strong> que seadapten al tiempo <strong>de</strong> ejecución y al sistema <strong>de</strong> tipos común. Sin embargo, <strong>para</strong> que los<strong>programadores</strong> puedan hacer que un lenguaje sea compatible con CLS, no necesitan crear unagran cantidad <strong>de</strong> trabajo adicional. El objetivo <strong>de</strong> CLS es ser lo más pequeño posible a la vezque ofrece un extenso conjunto <strong>de</strong> funciones y tipos <strong>de</strong> datos.
<strong>Instrucciones</strong> <strong>de</strong> nomenclaturaLa existencia <strong>de</strong> un mo<strong>de</strong>lo <strong>de</strong> nomenclatura coherente, es uno <strong>de</strong> los elementos más importantesen cuanto a previsibilidad y capacidad <strong>de</strong> <strong>de</strong>scubrimiento en una biblioteca <strong>de</strong> clases. El uso y elconocimiento generalizados <strong>de</strong> estas instrucciones <strong>de</strong> nomenclatura <strong>de</strong>bería eliminar la mayoría <strong>de</strong>las preguntas más frecuentes <strong>de</strong> los usuarios. En este tema se proporcionan instrucciones <strong>de</strong>nomenclatura <strong>para</strong> los tipos <strong>de</strong> .NET Framework. En cada tipo, <strong>de</strong>berá tener en cuenta tambiénalgunas <strong>de</strong> las reglas generales con relación a los estilos <strong>de</strong> mayúsculas, distinción entremayúsculas y minúsculas y elección <strong>de</strong> palabras.
Estilos <strong>de</strong> mayúsculasUtilice las tres convenciones siguientes <strong>para</strong> poner en mayúsculas los i<strong>de</strong>ntificadores.# Mayúsculas y minúsculas PascalLa primera letra <strong>de</strong>l i<strong>de</strong>ntificador y la primera letra <strong>de</strong> las siguientes palabras concatenadas están enmayúsculas. El estilo <strong>de</strong> mayúsculas y minúsculas Pascal se pue<strong>de</strong> utilizar en i<strong>de</strong>ntificadores <strong>de</strong>tres o más caracteres. Por ejemplo:BackColor# Mayúsculas y minúsculas CamelLa primera letra <strong>de</strong>l i<strong>de</strong>ntificador está en minúscula y la primera letra <strong>de</strong> las siguientes palabrasconcatenadas en mayúscula. Por ejemplo:backColorMayúsculasTodas las letras <strong>de</strong>l i<strong>de</strong>ntificador van en mayúsculas. Utilice esta convención sólo <strong>para</strong>i<strong>de</strong>ntificadores que estén formados por dos o menos letras. Por ejemplo:System.IOSystem.Web.UIA<strong>de</strong>más, pue<strong>de</strong> que sea necesario utilizar mayúsculas en los i<strong>de</strong>ntificadores <strong>para</strong> mantener lacompatibilidad con esquemas existentes <strong>de</strong> símbolos no administrados, don<strong>de</strong> los caracteres enmayúsculas se utilizan con frecuencia en valores <strong>de</strong> constantes y enumeraciones. En general, estossímbolos no <strong>de</strong>ben ser visibles fuera <strong>de</strong>l ensamblado en el que se utilizan.En la tabla siguiente se resumen las reglas <strong>de</strong> uso <strong>de</strong> mayúsculas y se proporcionan ejemplos <strong>de</strong>los diferentes tipos <strong>de</strong> i<strong>de</strong>ntificadores.I<strong>de</strong>ntificadorUsomayúsculasminúsculas<strong>de</strong>oEjemploClass Pascal AppDomainTipo Enum Pascal ErrorLevelValores enum Pascal FatalErrorEvento Pascal ValueChangeClaseexcepciones<strong>de</strong>PascalWebExceptionNota Termina siempre con el sufijo Exception.Campo estático <strong>de</strong>sólo lecturaPascalRedValue
InterfazPascalIDisposableNota Comienza siempre con el prefijo I.Método Pascal ToStringEspacionombres<strong>de</strong>PascalSystem.DrawingParámetro Camel typeNamePropiedad Pascal BackColorCampo <strong>de</strong>instancia protegidoCampo <strong>de</strong>instancia públicoCamelPascalredValueNota Se utiliza en contadas ocasiones. Espreferible utilizar una propiedad, en vez <strong>de</strong> uncampo <strong>de</strong> instancia protegido.RedValueNota Se utiliza en contadas ocasiones. Espreferible utilizar una propiedad, en vez <strong>de</strong> uncampo <strong>de</strong> instancia público.
Distinción <strong>de</strong> mayúsculas y minúsculasPara evitar confusiones y garantizar la interoperación entre lenguajes, siga estas reglas conrespecto a la distinción entre mayúsculas y minúsculas:• No utilice nombres que requieran distinción entre mayúsculas y minúsculas. Los componentesse <strong>de</strong>ben po<strong>de</strong>r utilizar en los lenguajes que distinguen, y en los que no distinguen, entremayúsculas y minúsculas. Los lenguajes que no hacen esta distinción no pue<strong>de</strong>n diferenciar,<strong>de</strong>ntro <strong>de</strong>l mismo contexto, dos nombres que difieren sólo en el uso <strong>de</strong> mayúsculas yminúsculas. Por consiguiente, se <strong>de</strong>be evitar esta situación en los componentes o clasescreados.• No <strong>de</strong>be crear dos espacios <strong>de</strong> nombres con nombres que difieran sólo en las mayúsculas yminúsculas. Por ejemplo, un lenguaje que no haga distinción entre mayúsculas y minúsculas nodistingue entre las dos <strong>de</strong>claraciones siguientes <strong>de</strong> espacio <strong>de</strong> nombres.namespace ee.cummings;namespace Ee.Cummings;• No <strong>de</strong>be crear una función con nombres <strong>de</strong> parámetros que difieran sólo en las mayúsculas yminúsculas. El siguiente ejemplo es incorrecto.void MyFunction(string a, string A)• No cree un espacio <strong>de</strong> nombres con nombres <strong>de</strong> tipos que difieran sólo en las mayúsculas yminúsculas. En el siguiente ejemplo, Point p y POINT p son nombres <strong>de</strong> tipo incorrectos ya quedifieren sólo en el uso <strong>de</strong> las mayúsculas y minúsculas.System.Windows.Forms.Point pSystem.Windows.Forms.POINT p• No <strong>de</strong>be crear un tipo con nombres <strong>de</strong> propieda<strong>de</strong>s que difieran sólo en las mayúsculas yminúsculas. En el siguiente ejemplo, int Color y int COLOR son nombres <strong>de</strong> propieda<strong>de</strong>sincorrectos ya que difieren sólo en el uso <strong>de</strong> las mayúsculas y minúsculas.int Color {get, set}int COLOR {get, set}• No <strong>de</strong>be crear un tipo con nombres <strong>de</strong> métodos que difieran sólo en las mayúsculas yminúsculas. En el siguiente ejemplo, calculate y Calculate son nombres <strong>de</strong> métodos incorrectosya que difieren sólo en el uso <strong>de</strong> las mayúsculas y minúsculas.void calculate()void Calculate()
AbreviaturasPara evitar confusiones y garantizar la interoperación entre lenguajes, siga estas reglas conrespecto a la utilización <strong>de</strong> abreviaturas:• No utilice abreviaturas ni contracciones como parte <strong>de</strong> nombres <strong>de</strong> i<strong>de</strong>ntificadores. Por ejemplo,utilice GetWindow en vez <strong>de</strong> GetWin.• No utilice acrónimos que no estén aceptados en el campo <strong>de</strong> la informática.• Si es necesario, utilice acrónimos conocidos <strong>para</strong> reemplazar nombres en frases largas. Porejemplo, utilice UI <strong>para</strong> interfaz <strong>de</strong> usuario y OLAP <strong>para</strong> procesamiento analítico en línea.• Cuando utilice acrónimos, utilice el estilo <strong>de</strong> mayúsculas y minúsculas Pascal o Camel enacrónimos <strong>de</strong> más <strong>de</strong> dos caracteres. Por ejemplo, use HtmlButton o htmlButton. Sin embargo,<strong>de</strong>berá utilizar mayúsculas en acrónimos que tengan sólo dos caracteres, por ejemploSystem.IO en vez <strong>de</strong> System.Io.• No utilice abreviaturas en nombres <strong>de</strong> i<strong>de</strong>ntificadores o parámetros. Si tiene que utilizarabreviaturas, utilice las Mayúsculas y minúsculas Camel en abreviaturas <strong>de</strong> dos o máscaracteres, aunque esto contradiga la abreviatura estándar <strong>de</strong> la palabra.Elección <strong>de</strong> palabraEvite utilizar nombres <strong>de</strong> clases que dupliquen los espacios <strong>de</strong> nombres utilizados habitualmente en.NET Framework. Por ejemplo, no utilice ninguno <strong>de</strong> los nombres siguientes como nombres <strong>de</strong>clases: System, Collections, Forms o UI. Vea Biblioteca <strong>de</strong> clases <strong>para</strong> obtener una lista <strong>de</strong> losespacios <strong>de</strong> nombres <strong>de</strong> .NET Framework.A<strong>de</strong>más, evite utilizar i<strong>de</strong>ntificadores que entren en conflicto con las siguientes palabras clave.Addhandler AddressOf Alias And AnsiAs Assembly Auto Base BooleanByRef Byte ByVal Call Uso <strong>de</strong>mayúsculas ominúsculasCatch CBool CByte CChar CDateCDec CDbl Char CInt ClassCLng CObj Const CShort CSngCStr CType Date Decimal DeclareDefault Delegate Dim Do DoubleEach Else ElseIf End EnumeraciónErase Error Evento Exit ExternalSourceFalse Finalize Finally Float ForFriend Función Get GetType GoToHandles If Implements Imports In
Inherits Integer Interfaz Is LetLib Like Long Loop MeMod Module MustInherit MustOverri<strong>de</strong> MyBaseMyClass Namespace Nueva Next NotNothing NotInheritable NotOverridable Object OnOpción Opcional Or Overloads OverridableOverri<strong>de</strong>s ParamArray Preserve Private PropertyProtected Public RaiseEvent ReadOnly ReDimRegion REM RemoveHandler Resume ReturnSelect Set Shadows Shared ShortSingle Static Paso Stop StringStructure Sub SyncLock Then ThrowA True Try TypeOf Unico<strong>de</strong>Until volatile When While WithWithEvents WriteOnly Xor eval extendsinstanceof package var
Evitar confusión <strong>de</strong> nombres <strong>de</strong> tiposLos distintos lenguajes <strong>de</strong> programación utilizan términos diferentes <strong>para</strong> i<strong>de</strong>ntificar los tiposadministrados básicos. Los diseñadores <strong>de</strong> bibliotecas <strong>de</strong> clases <strong>de</strong>ben evitar utilizar terminologíaespecífica <strong>de</strong>l lenguaje. Siga las reglas que se <strong>de</strong>scriben en esta sección <strong>para</strong> evitar la confusión <strong>de</strong>nombres <strong>de</strong> tipos.Utilice nombres que <strong>de</strong>scriban el significado <strong>de</strong>l tipo, en vez <strong>de</strong> nombres que <strong>de</strong>scriban el tipo. En elcaso poco frecuente <strong>de</strong> que un parámetro no tenga ningún otro significado semántico aparte <strong>de</strong>lsignificado <strong>de</strong>l tipo, utilice un nombre genérico. Por ejemplo, una clase que admite la escritura <strong>de</strong>diversidad <strong>de</strong> tipos <strong>de</strong> datos en una secuencia pue<strong>de</strong> tener los métodos siguientes.[Visual Basic]Sub Write(value As Double);Sub Write(value As Single);Sub Write(value As Long);Sub Write(value As Integer);Sub Write(value As Short);[C#]void Write(double value);void Write(float value);void Write(long value);void Write(int value);void Write(short value);No cree nombres <strong>de</strong> métodos con terminología específica <strong>de</strong>l lenguaje, como se muestra en elsiguiente ejemplo.[Visual Basic]Sub Write(doubleValue As Double);Sub Write(singleValue As Single);Sub Write(longValue As Long);Sub Write(integerValue As Integer);Sub Write(shortValue As Short);[C#]void Write(double doubleValue);void Write(float floatValue);void Write(long longValue);void Write(int intValue);void Write(short shortValue);
En el caso excepcional <strong>de</strong> que sea necesario crear un método con un nombre único <strong>para</strong> cada tipobásico <strong>de</strong> datos, utilice nombres <strong>de</strong> tipos universales. En la tabla siguiente se muestran los nombres<strong>de</strong> tipos básicos y las sustituciones universales.Nombre<strong>de</strong> tipo C#Nombre <strong>de</strong>tipo VisualBasicNombre <strong>de</strong>tipo JScriptNombre <strong>de</strong>tipo VisualC++RepresentaciónIlasm.exeNombre <strong>de</strong>tipouniversalsbyte SByte sByte char int8 SBytebyte Byte byte unsigned char unsigned int8 Byteshort Short short short int16 Int16ushort UInt16 ushort unsignedshortunsigned int16UInt16int Integer int int int32 Int32uint UInt32 uint unsigned int unsigned int32 UInt32long Long long __int64 int64 Int64ulong UInt64 ulong unsigned__int64unsigned int64UInt64float Single float float float32 Singledouble Double double double float64 Doublebool Boolean boolean bool bool Booleanchar Char char wchar_t char Charstring String string String string Stringobject Object object Object object ObjectPor ejemplo, una clase que admita la lectura <strong>de</strong> diversidad <strong>de</strong> tipos <strong>de</strong> datos en una secuenciapue<strong>de</strong> tener los siguientes métodos.[Visual Basic]ReadDouble()As DoubleReadSingle()As SingleReadInt64()As LongReadInt32()As IntegerReadInt16()As Short[C#]double ReadDouble();float ReadSingle();long ReadInt64();int ReadInt32();short ReadInt16();
Es preferible utilizar el ejemplo anterior, en vez <strong>de</strong> la alternativa específica <strong>de</strong> lenguaje que semuestra a continuación.[Visual Basic]ReadDouble()As DoubleReadSingle()As SingleReadLong()As LongReadInteger()As IntegerReadShort()As Short[C#]double ReadDouble();float ReadFloat();long ReadLong();int ReadInt();short ReadShort();
<strong>Instrucciones</strong> <strong>de</strong> nomenclatura <strong>de</strong> espacios <strong>de</strong>nombresPor regla general, en los espacios <strong>de</strong> nombres se utiliza el nombre <strong>de</strong> la compañía seguido <strong>de</strong>lnombre <strong>de</strong> la tecnología y, opcionalmente, la característica y el diseño como se muestra acontinuación.CompanyName.TechnologyName[.Feature][.Design]Por ejemplo:Microsoft.MediaMicrosoft.Media.DesignAl incluir un prefijo en los nombres <strong>de</strong> espacios <strong>de</strong> nombres que contengan el nombre <strong>de</strong> unacompañía o marca <strong>de</strong> reconocido prestigio, se evita la posibilidad <strong>de</strong> publicar dos espacios <strong>de</strong>nombres que tengan el mismo nombre. Por ejemplo, Microsoft.Office es un prefijo a<strong>de</strong>cuado <strong>para</strong>las clases <strong>de</strong> automatización <strong>de</strong> Office que proporciona Microsoft.Utilice un nombre <strong>de</strong> tecnología reconocida y estable en el segundo nivel <strong>de</strong> un nombre jerárquico.Utilice una jerarquía organizativa como base <strong>para</strong> la jerarquía <strong>de</strong> espacios <strong>de</strong> nombres. Asigne unnombre a un espacio <strong>de</strong> nombres que contenga los tipos que proporcionan funcionalida<strong>de</strong>s entiempo <strong>de</strong> diseño <strong>para</strong> un espacio <strong>de</strong> nombres base con el sufijo .Design. Por ejemplo, el Espacio<strong>de</strong> nombres System.Windows.Forms.Design contiene las clases <strong>de</strong> diseñador y clases relacionadas<strong>para</strong> diseñar aplicaciones basadas en System.Windows.Forms.Un espacio <strong>de</strong> nombres anidado <strong>de</strong>be tener una <strong>de</strong>pen<strong>de</strong>ncia en los tipos <strong>de</strong>l espacio <strong>de</strong> nombrescontenedor. Por ejemplo, las clases <strong>de</strong> System.Web.UI.Design <strong>de</strong>pen<strong>de</strong>n <strong>de</strong> las clases <strong>de</strong>System.Web.UI. No obstante, las clases <strong>de</strong> System.Web.UI no <strong>de</strong>pen<strong>de</strong>n <strong>de</strong> las clases enSystem.Web.UI.Design.En los espacios <strong>de</strong> nombres, se <strong>de</strong>be utilizar el estilo <strong>de</strong> Mayúsculas y minúsculas Pascal y se<strong>para</strong>rlos componentes lógicos con puntos, como en Microsoft.Office.PowerPoint. Si la marca utilizada noemplea la regla <strong>de</strong> mayúsculas y minúsculas tradicional, siga el sistema que utiliza la marca,aunque no siga la regla Pascal. Por ejemplo, los espacios <strong>de</strong> nombres NeXT.WebObjects yee.cummings muestran las variaciones correctas <strong>de</strong> utilización <strong>de</strong> mayúsculas y minúsculas conrespecto a la regla Pascal.Utilice los nombres <strong>de</strong> espacios <strong>de</strong> nombres en plural, siempre que sea correcto semánticamente.Por ejemplo, utilice System.Collections en vez <strong>de</strong> System.Collection. Las excepciones a esta reglason los nombres y abreviaturas <strong>de</strong> marcas. Por ejemplo, utilice System.IO en vez <strong>de</strong> System.IOs.No utilice el mismo nombre <strong>para</strong> un espacio <strong>de</strong> nombres y <strong>para</strong> una clase. Por ejemplo, noproporcione un espacio <strong>de</strong> nombres Debug y una clase Debug.Por último, tenga en cuenta que el nombre <strong>de</strong> un espacio <strong>de</strong> nombres no tiene que ser análogo alnombre <strong>de</strong>l ensamblado. Por ejemplo, si asigna el nombre MyCompany.MyTechnology.dll a unensamblado, no es necesario que contenga un espacio <strong>de</strong> nombres MyCompany.MyTechnology.
<strong>Instrucciones</strong> <strong>de</strong> nomenclatura <strong>de</strong> clasesEn las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> nomenclatura <strong>de</strong> clases:• Utilice un sustantivo o un sintagma nominal <strong>para</strong> asignar un nombre a una clase.• Utilice el estilo <strong>de</strong> Mayúsculas y minúsculas Pascal.• Utilice las abreviaturas con mo<strong>de</strong>ración.• No utilice un prefijo <strong>de</strong> tipo, como C <strong>para</strong> clase, en un nombre <strong>de</strong> clase. Utilice, por ejemplo, elnombre <strong>de</strong> clase FileStream en vez <strong>de</strong> CFileStream.• No utilice el carácter <strong>de</strong> subrayado (_).• De vez en cuando, es necesario proporcionar un nombre <strong>de</strong> clase que comience con la letra I,aunque la clase no sea una interfaz. Esto es correcto siempre que I sea la primera letra <strong>de</strong> unapalabra que forme parte <strong>de</strong>l nombre <strong>de</strong> la clase. Por ejemplo, I<strong>de</strong>ntityStore es un nombre <strong>de</strong>clase correcto.• Cuando sea apropiado, utilice una palabra compuesta en el nombre <strong>de</strong> una clase <strong>de</strong>rivada. Lasegunda parte <strong>de</strong>l nombre <strong>de</strong> la clase <strong>de</strong>rivada <strong>de</strong>be ser el nombre <strong>de</strong> la clase base. Porejemplo, ApplicationException es un nombre correcto <strong>para</strong> una clase <strong>de</strong>rivada <strong>de</strong> la claseException, pues ApplicationException es una clase <strong>de</strong> Exception. En esta regla se <strong>de</strong>be utilizarla lógica. Por ejemplo, Button es un nombre a<strong>de</strong>cuado <strong>para</strong> una clase <strong>de</strong>rivada <strong>de</strong> Control.Aunque un botón es una clase <strong>de</strong> control, si incluye Control como parte <strong>de</strong>l nombre <strong>de</strong> unaclase alargaría el nombre innecesariamente.A continuación, se incluyen algunos ejemplos <strong>de</strong> clases con nombres correctos:[Visual Basic]Public Class FileStreamPublic Class ButtonPublic Class String[C#]public class FileStreampublic class Buttonpublic class String
<strong>Instrucciones</strong> <strong>de</strong> nomenclatura <strong>de</strong> interfacesEn las reglas siguientes se <strong>de</strong>scriben las pautas <strong>de</strong> nomenclatura <strong>de</strong> interfaces:• Asigne nombres a interfaces utilizando sustantivos, sintagmas nominales o adjetivos que<strong>de</strong>scriban su comportamiento. Por ejemplo, en el nombre <strong>de</strong> la interfaz IComponent se utilizaun sustantivo <strong>de</strong>scriptivo. En el nombre <strong>de</strong> la interfaz ICustomAttributeProvi<strong>de</strong>r se utiliza unsintagma nominal. En el nombre IPersistable se utiliza un adjetivo.• Utilice el estilo <strong>de</strong> Mayúsculas y minúsculas Pascal.• Utilice las abreviaturas con mo<strong>de</strong>ración.• Incluya un prefijo con la letra I en los nombres <strong>de</strong> interfaces <strong>para</strong> indicar que el tipo es unainterfaz.• Utilice nombres similares cuando <strong>de</strong>fina un par clase/interfaz, don<strong>de</strong> la clase es unaimplementación estándar <strong>de</strong> la interfaz. Los nombres <strong>de</strong>ben ser distintos sólo en el prefijo I <strong>de</strong>lnombre <strong>de</strong> la interfaz.• No utilice el carácter <strong>de</strong> subrayado (_).A continuación, se incluyen algunos ejemplos <strong>de</strong> interfaces con nombres correctos:[Visual Basic]Public Interface IServiceProvi<strong>de</strong>rPublic Interface IFormatable[C#]public interface IServiceProvi<strong>de</strong>rpublic interface IFormatableEn el siguiente ejemplo <strong>de</strong> código se muestra cómo <strong>de</strong>finir la interfaz IComponent y suimplementación estándar, la clase Component.[Visual Basic]Public Interface IComponent' Implementation co<strong>de</strong> goes here.End InterfacePublic Class ComponentImplements IComponent' Implementation co<strong>de</strong> goes here.End Class[C#]public interface IComponent{// Implementation co<strong>de</strong> goes here.}public class Component: IComponent{// Implementation co<strong>de</strong> goes here.}
<strong>Instrucciones</strong> <strong>de</strong> nomenclatura <strong>de</strong> atributosDeberá agregar siempre el sufijo Attribute a las clases <strong>de</strong> atributos personalizados. A continuación,se incluye un ejemplo <strong>de</strong> un nombre correcto <strong>de</strong> clase <strong>de</strong> atributo:[Visual Basic]Public Class ObsoleteAttribute[C#]public class ObsoleteAttribute{}<strong>Instrucciones</strong> <strong>de</strong> nomenclatura <strong>de</strong> tipos <strong>de</strong>enumeraciónEl tipo <strong>de</strong> valor <strong>de</strong> enumeración (Enum) se hereda <strong>de</strong> la Clase Enum. En las reglas siguientes se<strong>de</strong>scriben las instrucciones <strong>de</strong> nomenclatura <strong>de</strong> enumeraciones:• Utilice el estilo <strong>de</strong> Mayúsculas y minúsculas Pascal en los nombres <strong>de</strong> valores y tipos Enum.• Utilice las abreviaturas con mo<strong>de</strong>ración.• No utilice el sufijo Enum en nombres <strong>de</strong> tipo Enum.• Utilice un nombre en singular <strong>para</strong> la mayoría <strong>de</strong> los tipos Enum, pero utilice un nombre enplural <strong>para</strong> los tipos Enum que son campos <strong>de</strong> bits.• Agregue siempre FlagsAttribute a un tipo Enum <strong>de</strong> campo <strong>de</strong> bits.<strong>Instrucciones</strong> <strong>de</strong> nomenclatura <strong>de</strong> campos estáticosEn las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> nomenclatura <strong>de</strong> campos estáticos:• Utilice sustantivos, sintagmas nominales o abreviaturas <strong>de</strong> nombres al asignar nombres acampos estáticos.• Utilice el estilo <strong>de</strong> Mayúsculas y minúsculas Pascal.• No utilice un prefijo <strong>de</strong> notación húngara en nombres <strong>de</strong> campos estáticos.• Se recomienda utilizar propieda<strong>de</strong>s estáticas en lugar <strong>de</strong> campos estáticos públicos cada vezque sea posible.
<strong>Instrucciones</strong> <strong>de</strong> nomenclatura <strong>de</strong> parámetrosEs importante seguir estas instrucciones <strong>de</strong> nomenclatura <strong>de</strong> parámetros, ya que las herramientas<strong>de</strong> diseño visual que proporcionan ayuda contextual y funcionalidad <strong>de</strong> exploración <strong>de</strong> clasesmuestran en el diseñador los nombres <strong>de</strong> los parámetros <strong>de</strong> métodos a los usuarios. En las reglassiguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> nomenclatura <strong>de</strong> parámetros:• Utilice el estilo <strong>de</strong> Mayúsculas y minúsculas Camel <strong>para</strong> los nombres <strong>de</strong> parámetros.• Utilice nombres <strong>de</strong> parámetros <strong>de</strong>scriptivos. Los nombres <strong>de</strong> parámetros <strong>de</strong>ben ser losuficientemente <strong>de</strong>scriptivos como <strong>para</strong> que el nombre y el tipo <strong>de</strong>l parámetro se puedan utilizar<strong>para</strong> <strong>de</strong>terminar su significado en la mayoría <strong>de</strong> los escenarios. Por ejemplo, las herramientas<strong>de</strong> diseño visual que proporcionan ayuda contextual muestran los parámetros <strong>de</strong> los métodos alos <strong>programadores</strong> mientras escriben. Por tanto, los nombres <strong>de</strong> los parámetros <strong>de</strong>berían ser losuficientemente <strong>de</strong>scriptivos en este escenario como <strong>para</strong> permitir a los <strong>programadores</strong>suministrar los parámetros a<strong>de</strong>cuados.• Utilice nombres que <strong>de</strong>scriban el significado <strong>de</strong>l parámetro, en vez <strong>de</strong> nombres que <strong>de</strong>scriban eltipo <strong>de</strong> parámetro. Las herramientas <strong>de</strong> <strong>de</strong>sarrollo <strong>de</strong>ben proporcionar información <strong>de</strong>scriptivasobre el tipo <strong>de</strong> parámetro. Por tanto, el nombre <strong>de</strong>l parámetro también sirve <strong>para</strong> <strong>de</strong>scribir susignificado. Utilice los nombres <strong>de</strong> parámetros basados en tipos con mo<strong>de</strong>ración y sólo cuandosea correcto.• No utilice parámetros reservados. Los parámetros reservados son parámetros privados que sepue<strong>de</strong>n exponer en futuras versiones, si así se precisa. En vez <strong>de</strong> esto, si se necesitan másdatos en una versión futura <strong>de</strong> la biblioteca <strong>de</strong> clases, agregue una nueva sobrecarga <strong>para</strong> unmétodo.• No incluya un prefijo en los nombres <strong>de</strong> parámetros con notación húngara <strong>de</strong> tipo.A continuación, se incluyen algunos ejemplos <strong>de</strong> parámetros con nombres correctos:[Visual Basic]GetType(typeName As String)As TypeFormat(format As String, args() As object)As String[C#]Type GetType(string typeName)string Format(string format, object[] args)
<strong>Instrucciones</strong> <strong>de</strong> nomenclatura <strong>de</strong> métodosEn las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> nomenclatura <strong>de</strong> métodos:• Utilice verbos o sintagmas verbales al asignar nombres a los métodos.• Utilice el estilo <strong>de</strong> Mayúsculas y minúsculas Pascal.A continuación, se incluyen algunos ejemplos <strong>de</strong> métodos con nombres correctos:RemoveAll()GetCharArray()Invoke()<strong>Instrucciones</strong> <strong>de</strong> nomenclatura <strong>de</strong> propieda<strong>de</strong>sEn las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> nomenclatura <strong>de</strong> propieda<strong>de</strong>s:• Utilice un sustantivo o un sintagma nominal al asignar nombres a las propieda<strong>de</strong>s.• Utilice el estilo <strong>de</strong> Mayúsculas y minúsculas Pascal.• No utilice la notación húngara.• Es conveniente crear una propiedad con el mismo nombre que el tipo subyacentecorrespondiente. Por ejemplo, si <strong>de</strong>clara una propiedad con el nombre Color, el tipo <strong>de</strong>propiedad también <strong>de</strong>berá llamarse Color. Vea el ejemplo que se muestra más a<strong>de</strong>lante en estetema.En el siguiente ejemplo <strong>de</strong> código se muestra cómo asignar nombres correctos a propieda<strong>de</strong>s.[Visual Basic]Public Class SampleClassPublic Property BackColor As Color' Co<strong>de</strong> for Get and Set accessors goes here.End PropertyEnd Class[C#]public class SampleClass{ public Color BackColor}{ // Co<strong>de</strong> for Get and Set accessors goes here. }En el siguiente ejemplo <strong>de</strong> código se muestra cómo proporcionar una propiedad con el mismonombre que el tipo.[Visual Basic]Public Enum Color' Insert co<strong>de</strong> for Enum here.End Enum
Public Class ControlPublic Property Color As ColorGet' Insert co<strong>de</strong> here.End GetSet' Insert co<strong>de</strong> here.End SetEnd PropertyEnd Class[C#]public enum Color{ // Insert co<strong>de</strong> for Enum here.}public class Control{ public Color Color{ get {// Insert co<strong>de</strong> here.}set {// Insert co<strong>de</strong> here.}}}El siguiente ejemplo <strong>de</strong> código es incorrecto ya que la propiedad Color es <strong>de</strong> tipo Integer.[Visual Basic]Public Enum Color' Insert co<strong>de</strong> for Enum here.End EnumPublic Class ControlPublic Property Color As IntegerGet' Insert co<strong>de</strong> here.End GetSet' Insert co<strong>de</strong> here.End SetEnd PropertyEnd Class[C#]public enum Color {// Insert co<strong>de</strong> for Enum here.}
public class Control{}public int Color{}get {// Insert co<strong>de</strong> here.}set {// Insert co<strong>de</strong> here.}En el ejemplo incorrecto, no es posible hacer referencia a los miembros <strong>de</strong> la enumeración Color.Color.Xxx se interpretará como que tiene acceso a un miembro que primero obtiene el valor <strong>de</strong> lapropiedad Color (tipo Integer en Visual Basic o tipo int en C#) y que, a continuación, tiene acceso aun miembro <strong>de</strong> ese valor (que tendrá que ser un miembro <strong>de</strong> instancia <strong>de</strong> System.Int32).<strong>Instrucciones</strong> <strong>de</strong> nomenclatura <strong>de</strong> eventosEn las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> nomenclatura <strong>de</strong> eventos:• Utilice el estilo <strong>de</strong> Mayúsculas y minúsculas Pascal.• No utilice la notación húngara.• Utilice un sufijo EventHandler en nombres <strong>de</strong> controladores <strong>de</strong> eventos.• Especifique dos parámetros <strong>de</strong>nominados sen<strong>de</strong>r y e. El parámetro sen<strong>de</strong>r representa al objetoque produjo el evento. El parámetro sen<strong>de</strong>r es siempre <strong>de</strong> tipo object, aunque sea posibleutilizar un tipo más específico. El estado asociado con el evento está encapsulado en unainstancia <strong>de</strong> una clase <strong>de</strong> eventos <strong>de</strong>nominada e. Use una clase <strong>de</strong> eventos apropiada yespecífica <strong>para</strong> el tipo <strong>de</strong> parámetro e.• Asigne un nombre a la clase <strong>de</strong> argumentos <strong>de</strong>l evento con el sufijo EventArgs.• Es conveniente asignar los nombres <strong>de</strong> eventos con un verbo. Por ejemplo, nombres <strong>de</strong>eventos correctos incluyen Clicked, Painting y DroppedDown.• Utilice un verbo en gerundio <strong>para</strong> crear un nombre <strong>de</strong> evento que exprese el concepto <strong>de</strong>evento anterior, y un tiempo en pasado <strong>para</strong> representar un evento posterior. Por ejemplo, unevento Close que se pue<strong>de</strong> cancelar, <strong>de</strong>be tener un evento Closing y un evento Closed. Noutilice el mo<strong>de</strong>lo <strong>de</strong> nomenclatura BeforeXxx/AfterXxx.• No utilice prefijo ni sufijo en la <strong>de</strong>claración <strong>de</strong> evento en el tipo. Por ejemplo, utilice Close en vez<strong>de</strong> OnClose.• En general, se <strong>de</strong>be proporcionar un método protegido <strong>de</strong>nominado OnXxx en los tipos coneventos que se pue<strong>de</strong>n reemplazar en una clase <strong>de</strong>rivada. Este método <strong>de</strong>be tener sólo elparámetro <strong>de</strong> evento e, ya que el remitente es siempre la instancia <strong>de</strong>l tipo.En el ejemplo siguiente se muestra un controlador <strong>de</strong> eventos con nombre y parámetros correctos.[Visual Basic]Public Delegate Sub MouseEventHandler(sen<strong>de</strong>r As Object, e As MouseEventArgs)[C#]
public <strong>de</strong>legate void MouseEventHandler(object sen<strong>de</strong>r, MouseEventArgs e);En el siguiente ejemplo se muestra una clase <strong>de</strong> argumentos <strong>de</strong> evento con nombre correcto.[Visual Basic]Public Class MouseEventArgsInherits EventArgsDim x As IntegerDim y As IntegerPublic Sub New MouseEventArgs(x As Integer, y As Integer)me.x = xme.y = yEnd SubPublic Property X As IntegerGetReturn xEnd GetEnd PropertyPublic Property Y As IntegerGetReturn yEnd GetEnd PropertyEnd Class[C#]public class MouseEventArgs : EventArgs{int x;int y;public MouseEventArgs(int x, int y){ this.x = x; this.y = y; }public int X { get { return x; } }public int Y { get { return y; } }}
<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> miembros <strong>de</strong> clasesEn este tema se proporcionan las instrucciones <strong>de</strong> utilización <strong>de</strong> miembros <strong>de</strong> clases en lasbibliotecas <strong>de</strong> clases.<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> propieda<strong>de</strong>sDetermina si es más a<strong>de</strong>cuado utilizar una propiedad o un método en función <strong>de</strong> las necesida<strong>de</strong>s.Para obtener más información sobre la elección entre propieda<strong>de</strong>s o métodos, vea Propieda<strong>de</strong>s ymétodos.Elija un nombre <strong>para</strong> la propiedad siguiendo las <strong>Instrucciones</strong> <strong>de</strong> nomenclatura <strong>de</strong> propieda<strong>de</strong>srecomendadas.Cuando tenga acceso a una propiedad mediante el <strong>de</strong>scriptor <strong>de</strong> acceso set, conserve el valor <strong>de</strong> lapropiedad antes <strong>de</strong> cambiarlo. De este modo, se garantiza que los datos no se per<strong>de</strong>rán si el<strong>de</strong>scriptor <strong>de</strong> acceso set inicia una excepción.Problemas <strong>de</strong> estado <strong>de</strong> propieda<strong>de</strong>sPermita que las propieda<strong>de</strong>s se establezcan en cualquier or<strong>de</strong>n. Las propieda<strong>de</strong>s <strong>de</strong>ben serin<strong>de</strong>pendientes con relación a otras propieda<strong>de</strong>s. Suele ocurrir a menudo que la función <strong>de</strong> unobjeto no se activa hasta que el programador <strong>de</strong>fine un conjunto <strong>de</strong> propieda<strong>de</strong>s específico; o hastaque el objeto tiene un estado <strong>de</strong>terminado. Si el objeto no tiene el estado correcto, la función no seactiva. Cuando el objeto tenga el estado correcto, la función se activará automáticamente sinnecesidad <strong>de</strong> una llamada explícita. La semántica es la misma in<strong>de</strong>pendientemente <strong>de</strong>l or<strong>de</strong>n en elque el programador establezca los valores <strong>de</strong> la propiedad, o <strong>de</strong> cómo consiga que el objeto tengael estado activo.Por ejemplo, un control TextBox pue<strong>de</strong> contener dos propieda<strong>de</strong>s relacionadas: DataSource yDataField. DataSource especifica el nombre <strong>de</strong> la tabla y DataField el nombre <strong>de</strong> la columna. Unavez especificadas las dos propieda<strong>de</strong>s, el control pue<strong>de</strong> enlazar automáticamente datos <strong>de</strong> la tablaa la propiedad Text <strong>de</strong>l control. En el siguiente ejemplo <strong>de</strong> código se muestran las propieda<strong>de</strong>s quese pue<strong>de</strong>n establecer en cualquier or<strong>de</strong>n.[Visual Basic]Dim t As New TextBox()t.DataSource = "Publishers"t.DataField = "AuthorID"' The data-binding feature is now active.[C#]TextBox t = new TextBox();t.DataSource = "Publishers";t.DataField = "AuthorID";// The data-binding feature is now active.Las propieda<strong>de</strong>s DataSource y DataField se pue<strong>de</strong>n establecer en cualquier or<strong>de</strong>n. Por tanto, elcódigo anterior es equivalente a lo siguiente:
[Visual Basic]Dim t As New TextBox()t.DataField = "AuthorID"t.DataSource = "Publishers"' The data-binding feature is now active.[C#]TextBox t = new TextBox();t.DataField = "AuthorID";t.DataSource = "Publishers";// The data-binding feature is now active.También se pue<strong>de</strong> establecer una propiedad como null (Nothing en Visual Basic) <strong>para</strong> indicar queno se especifica ningún valor.[Visual Basic]Dim t As New TextBox()t.DataField = "AuthorID"t.DataSource = "Publishers"' The data-binding feature is now active.t.DataSource = Nothing' The data-binding feature is now inactive.[C#]TextBox t = new TextBox();t.DataField = "AuthorID";t.DataSource = "Publishers";// The data-binding feature is now active.t.DataSource = null;// The data-binding feature is now inactive.En el siguiente ejemplo <strong>de</strong> código se muestra cómo realizar el seguimiento <strong>de</strong>l estado <strong>de</strong> la función<strong>de</strong> enlace <strong>de</strong> datos y activarla o <strong>de</strong>sactivarla automáticamente en el momento a<strong>de</strong>cuado.[Visual Basic]Public Class TextBoxPrivate m_dataSource As StringPrivate m_dataField As StringPrivate m_active As BooleanPublic Property DataSource() As String
GetReturn m_dataSourceEnd GetSetIf value m_dataSource Then' Set the property value first, in case activate fails.m_dataSource = value' Update active state.SetActive(( Not (m_dataSource Is Nothing) And Not (m_dataField Is Nothing)))End IfEnd SetEnd PropertyPublic Property DataField() As StringGetReturn m_dataFieldEnd GetSetIf value m_dataField Then' Set the property value first, in case activate fails.m_dataField = value' Update active state.SetActive(( Not (m_dataSource Is Nothing) And Not (m_dataField Is Nothing)))End IfEnd SetEnd PropertySub SetActive(m_value As Boolean)If value m_active ThenIf m_value ThenActivate()Text = dataBase.Value(m_dataField)ElseDeactivate()Text = ""End If' Set active only if successful.m_active = value
End IfEnd SubSub Activate()' Open database.End SubSub Deactivate()' Close database.End SubEnd Class[C#]public class TextBox{string dataSource;string dataField;bool active;public string DataSource{get{return dataSource;}set{if (value != dataSource){// Update active state.SetActive(value != null && dataField != null);dataSource = value;}}}public string DataField{
get{return dataField;}set{if (value != dataField){// Update active state.SetActive(dataSource != null && dataField != null);dataField = value;}}}void SetActive(Boolean value){if (value != active){if (value){ Activate();Text = dataBase.Value(dataField);}else{ Deactivate();Text = "";}// Set active only if successful.active = value;}}void Activate(){ // Open database. }}void Deactivate(){ // Close database. }
En el ejemplo anterior, la siguiente expresión <strong>de</strong>termina si el objeto está en un estado en el que lafunción <strong>de</strong> enlace <strong>de</strong> datos se activa automáticamente.[Visual Basic](Not (value Is Nothing) And Not (m_dataField Is Nothing))[C#]value != null && dataField != nullLa activación automática se <strong>de</strong>fine mediante la creación <strong>de</strong> un método que <strong>de</strong>termina si el objeto sepue<strong>de</strong> activar en función <strong>de</strong> su estado actual y, a continuación, se activa si es necesario.[Visual Basic]Sub UpdateActive(m_dataSource As String, m_dataField As String)SetActive(( Not (m_dataSource Is Nothing) And Not (m_dataField Is Nothing)))End Sub[C#]void UpdateActive(string dataSource, string dataField){ SetActive(dataSource != null && dataField != null);}Si tiene propieda<strong>de</strong>s relacionadas, como DataSource y DataMember, consi<strong>de</strong>re la posibilidad <strong>de</strong>implementar ISupportInitialize (Interfaz). De este modo, el diseñador (o el usuario) podrá llamar a losmétodos ISupportInitialize.BeginInit e ISupportInitialize.EndInit al establecer varias propieda<strong>de</strong>s <strong>para</strong>que el componente pueda proce<strong>de</strong>r a optimizar. En el ejemplo anterior, ISupportInitialize pue<strong>de</strong>evitar los intentos innecesarios <strong>de</strong> obtener acceso a la base <strong>de</strong> datos hasta que se haya realizadocorrectamente la configuración.La expresión que aparece en este método indica las partes <strong>de</strong>l mo<strong>de</strong>lo <strong>de</strong> objetos que se <strong>de</strong>benexaminar <strong>para</strong> exigir estas transiciones <strong>de</strong> estado. En este caso, afecta a las propieda<strong>de</strong>sDataSource y DataField. Para obtener más información sobre la elección entre propieda<strong>de</strong>s ométodos, vea Propieda<strong>de</strong>s frente a métodos.Provocar eventos PropertyChangedLos componentes <strong>de</strong>ben provocar eventos PropertyChanged, si se <strong>de</strong>sea notificar a losconsumidores cuando se cambia la propiedad <strong>de</strong>l componente mediante programación. Laconvención <strong>de</strong> nomenclatura <strong>para</strong> un evento PropertyChanged es agregar el sufijo Changed alnombre <strong>de</strong> la propiedad, como en TextChanged. Por ejemplo, un control pue<strong>de</strong> provocar un eventoTextChanged cuando se cambia la propiedad <strong>de</strong> texto. Para provocar este evento, se pue<strong>de</strong> utilizarla rutina auxiliar protegida RaiseChanged. No obstante, es posible que la sobrecarga<strong>para</strong> provocar el evento PropertyChanged y agregar un elemento a la tabla hash no compense. Enel siguiente ejemplo <strong>de</strong> código se muestra la implementación <strong>de</strong> una rutina auxiliar en un eventoPropertyChanged.[Visual Basic]Class ControlInherits ComponentPrivate m_text As StringPublic Property Text() As String
GetReturn m_textEnd GetSetIf Not m_text.Equals(value) Thenm_text = valueRaiseTextChanged()End IfEnd SetEnd PropertyEnd Class[C#]class Control: Component{}string text;public string Text{}get{ return text;}set{}if (!text.Equals(value)){ text = value;}RaiseTextChanged();El enlace <strong>de</strong> datos utiliza este mo<strong>de</strong>lo <strong>para</strong> permitir el enlace <strong>de</strong> la propiedad en ambos sentidos.Sin los eventos Changed y RaiseChanged, el enlace <strong>de</strong> datos funciona enun sentido; si la base <strong>de</strong> datos cambia, la propiedad se actualiza. Cada propiedad que provoca elevento Changed <strong>de</strong>be proporcionar los metadatos que indican que la propiedad admiteel enlace <strong>de</strong> datos.Conviene provocar eventos <strong>de</strong> cambio/cambiados, si el valor <strong>de</strong> una propiedad cambia <strong>de</strong>bido aoperaciones externas. Estos eventos indican al programador que el valor <strong>de</strong> una propiedad estácambiando o ha cambiado como resultado <strong>de</strong> una operación y no mediante una llamada al métodoen el objeto.
La propiedad Text <strong>de</strong> un control Edit es un ejemplo claro. Mientras el usuario escribe informaciónen el control, el valor <strong>de</strong> la propiedad cambia automáticamente. Se provoca un evento antes <strong>de</strong> queel valor <strong>de</strong> la propiedad cambie. No se pasa ni el valor antiguo ni el nuevo, y el programador pue<strong>de</strong>cancelar el evento iniciando una excepción. El nombre <strong>de</strong>l evento es el nombre <strong>de</strong> la propiedadseguido <strong>de</strong>l sufijo Changing. En el siguiente ejemplo <strong>de</strong> código se muestra un evento <strong>de</strong> cambio.[Visual Basic]Class EditInherits ControlPublic Property Text() As StringGetReturn m_textEnd GetSetIf m_text value ThenOnTextChanging(Event.Empty)m_text = valueEnd IfEnd SetEnd PropertyEnd Class[C#]class Edit : Control{public string Text{get{ return text; }set{ if (text != value){ OnTextChanging(Event.Empty);text = value;}}}}
También se provoca un evento <strong>de</strong>spués <strong>de</strong> cambiar el valor <strong>de</strong> la propiedad. Este evento no sepue<strong>de</strong> cancelar. El nombre <strong>de</strong>l evento es el nombre <strong>de</strong> la propiedad seguido <strong>de</strong>l sufijo Changed.También se <strong>de</strong>be provocar el evento PropertyChanged genérico. El mo<strong>de</strong>lo <strong>para</strong> provocar estosdos eventos es provocar el evento específico <strong>de</strong>s<strong>de</strong> el método OnPropertyChanged. En elsiguiente ejemplo se muestra el uso <strong>de</strong>l método OnPropertyChanged.[Visual Basic]Class EditInherits ControlPublic Property Text() As StringGetReturn m_textEnd GetSetIf m_text value ThenOnTextChanging(Event.Empty)m_text = valueRaisePropertyChangedEvent(Edit.ClassInfo. m_text)End IfEnd SetEnd PropertyProtected Sub OnPropertyChanged(e As PropertyChangedEventArgs)If e.PropertyChanged.Equals(Edit.ClassInfo. m_text) ThenOnTextChanged(Event.Empty)End IfIf Not (onPropertyChangedHandler Is Nothing) ThenonPropertyChangedHandler(Me, e)End IfEnd SubEnd Class[C#]class Edit : Control{public string Text{ get{ return text; }set{ if (text != value)
}}{ OnTextChanging(Event.Empty);text = value;RaisePropertyChangedEvent(Edit.ClassInfo.text);}}protected void OnPropertyChanged(PropertyChangedEventArgs e){if (e.PropertyChanged.Equals(Edit.ClassInfo.text))OnTextChanged(Event.Empty);if (onPropertyChangedHandler != null)onPropertyChangedHandler(this, e);}En algunos casos el valor subyacente <strong>de</strong> una propiedad no se almacena como campo, estocomplica el seguimiento <strong>de</strong> los cambios <strong>de</strong> valores. Al provocar el evento <strong>de</strong> cambio, busque todoslos lugares en los que el valor <strong>de</strong> la propiedad pue<strong>de</strong> cambiar y proporcione la capacidad <strong>de</strong>cancelar el evento. Por ejemplo, el control Edit <strong>de</strong>l ejemplo anterior no es <strong>de</strong>l todo exacto porque elvalor Text está almacenado en realidad en el i<strong>de</strong>ntificador <strong>de</strong> ventana (HWND). Para provocar elevento TextChanging, <strong>de</strong>be examinar los mensajes <strong>de</strong> Windows <strong>para</strong> <strong>de</strong>terminar cuando pue<strong>de</strong>cambiar el texto; y permitir que se inicie una excepción en OnTextChanging <strong>para</strong> cancelar elevento. Si es muy difícil proporcionar un evento <strong>de</strong> cambio, se pue<strong>de</strong> admitir sólo el eventocambiado.Propieda<strong>de</strong>s frente a métodosLos diseñadores <strong>de</strong> bibliotecas <strong>de</strong> clases <strong>de</strong>ben <strong>de</strong>cidir a menudo entre implementar un miembro <strong>de</strong>la clase como una propiedad o como un método. En general, los métodos representan acciones ylas propieda<strong>de</strong>s representan datos. Utilice las instrucciones siguientes <strong>para</strong> <strong>de</strong>cidir entre estasopciones.• Utilice una propiedad cuando el miembro sea un miembro <strong>de</strong> datos lógico. En las siguientes<strong>de</strong>claraciones <strong>de</strong> miembro, Name es una propiedad porque es un miembro lógico <strong>de</strong> la clase.[Visual Basic]Public Property Name As StringGetReturn m_nameEnd GetSetm_name = value
End SetEnd Property[C#]public string Nameget{ return name; }set{ name = value; }• Utilice un método cuando:• La operación es una conversión, como Object.ToString.• La operación es bastante costosa y, por tanto, <strong>de</strong>sea ponerse en contacto con el usuario<strong>para</strong> que tenga en cuenta que <strong>de</strong>be almacenar el resultado en la caché.• Obtiene el valor <strong>de</strong> una propiedad mediante el <strong>de</strong>scriptor <strong>de</strong> acceso get y esta acción tieneefectos secundarios visibles.• Al llamar al miembro dos veces seguidas da lugar a resultados distintos.• El or<strong>de</strong>n <strong>de</strong> ejecución es importante. Tenga en cuenta que las propieda<strong>de</strong>s <strong>de</strong> un tipo se<strong>de</strong>ben po<strong>de</strong>r establecer y recuperar en cualquier or<strong>de</strong>n.• El miembro es estático, pero <strong>de</strong>vuelve un valor que se pue<strong>de</strong> cambiar.• El miembro <strong>de</strong>vuelve una matriz. Las propieda<strong>de</strong>s que <strong>de</strong>vuelven matrices pue<strong>de</strong>n ser muyimprecisas. Por lo general, es necesario <strong>de</strong>volver una copia <strong>de</strong> la matriz interna <strong>para</strong> que elusuario no pueda cambiar el estado interno. Esto junto con el hecho <strong>de</strong> que el usuariopue<strong>de</strong> creer fácilmente que es una propiedad indizada, conduce a un código ineficaz. En elsiguiente ejemplo <strong>de</strong> código, cada llamada a la propiedad Methods crea una copia <strong>de</strong> lamatriz. Como resultado, se crearán 2 n +1 copias <strong>de</strong> la matriz en el siguiente bucle.[Visual Basic]Dim type As Type = ' Get a type.Dim i As IntegerFor i = 0 To type.Methods.Length - 1If type.Methods(i).Name.Equals("text") Then' Perform some operation.End IfNext i[C#]Type type = // Get a type.for (int i = 0; i < type.Methods.Length; i++){if (type.Methods[i].Name.Equals ("text"))
}{ // Perform some operation. }En el ejemplo siguiente se muestra el uso correcto <strong>de</strong> propieda<strong>de</strong>s y métodos.[Visual Basic]Class Connection' The following three members should be properties' because they can be set in any or<strong>de</strong>r.Property DNSName() As String' Co<strong>de</strong> for get and set accessors goes here.End PropertyProperty UserName() As String' Co<strong>de</strong> for get and set accessors goes here.End PropertyProperty Password() As String'Co<strong>de</strong> for get and set accessors goes here.End Property' The following member should be a method' because the or<strong>de</strong>r of execution is important.' This method cannot be executed until after the' properties have been set.Function Execute() As Boolean[C#]class Connection{// The following three members should be properties// because they can be set in any or<strong>de</strong>r.string DNSName {get{};set{};}string UserName {get{};set{};}string Password {get{};set{};}// The following member should be a method// because the or<strong>de</strong>r of execution is important.// This method cannot be executed until after the// properties have been set.bool Execute ();}
Propieda<strong>de</strong>s <strong>de</strong> sólo lectura y escrituraSe <strong>de</strong>be utilizar una propiedad <strong>de</strong> sólo lectura cuando el usuario no pue<strong>de</strong> cambiar el miembro <strong>de</strong>datos lógicos <strong>de</strong> la propiedad. No utilice propieda<strong>de</strong>s <strong>de</strong> sólo escritura.Uso <strong>de</strong> propieda<strong>de</strong>s indizadasNota Las propieda<strong>de</strong>s indizadas también se conocen como indizadores.En las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> utilización <strong>de</strong> propieda<strong>de</strong>s indizadas:• Utilice una propiedad indizada cuando el miembro <strong>de</strong> datos lógico <strong>de</strong> la propiedad sea unamatriz.• Consi<strong>de</strong>re la utilización sólo <strong>de</strong> valores enteros o ca<strong>de</strong>nas <strong>para</strong> las propieda<strong>de</strong>s indizadas. Si eldiseño requiere otros tipos <strong>para</strong> las propieda<strong>de</strong>s indizadas, reconsi<strong>de</strong>re si representa unmiembro <strong>de</strong> datos lógico. Si no es así, use un método.• Consi<strong>de</strong>re usar sólo un índice. Si el diseño requiere varios índices, reconsi<strong>de</strong>re si representa unmiembro <strong>de</strong> datos lógico. Si no es así, use un método.• Utilice sólo una propiedad indizada por clase y convierta esta propiedad en el valorpre<strong>de</strong>terminado <strong>de</strong> esa clase. La compatibilidad con indizadores <strong>de</strong>l lenguaje <strong>de</strong> programaciónC# hace que se cumpla esta regla.• No utilice propieda<strong>de</strong>s indizadas no pre<strong>de</strong>terminadas. C# no lo permite.• Asigne el nombre Item a una propiedad indizada. Por ejemplo, vea la Propiedad DataGrid.Item.Siga esta regla, a no ser que haya un nombre más obvio <strong>para</strong> los usuarios como la propiedadChars <strong>de</strong> la clase String. En C#, los indizadores siempre adoptan el nombre Item.• No proporcione una propiedad indizada y un método que sean equivalentes semánticamente ados o más métodos sobrecargados. En el siguiente ejemplo <strong>de</strong> código, se <strong>de</strong>be cambiar lapropiedad Method al método GetMethod(string). Tenga en cuenta que esto no está permitido enC#.[Visual Basic]' Change the MethodInfo Type.Method property to a method.Property Type.Method(name As String) As MethodInfoFunction Type.GetMethod(name As String, ignoreCase As Boolean) As MethodInfo[C#]// Change the MethodInfo Type.Method property to a method.MethodInfo Type.Method[string name]MethodInfo Type.GetMethod (string name, Boolean ignoreCase)[Visual Basic]' The MethodInfo Type.Method property is changed to' the MethodInfo Type.GetMethod method.Function Type.GetMethod(name As String) As MethodInfoFunction Type.GetMethod(name As String, ignoreCase As Boolean) As MethodInfo[C#]
The MethodInfo Type.Method property is changed to// the MethodInfo Type.GetMethod method.MethodInfo Type.GetMethod(string name)MethodInfo Type.GetMethod (string name, Boolean ignoreCase)
<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> eventosEn las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> uso <strong>de</strong> eventos:• Elija un nombre <strong>para</strong> el evento siguiendo las <strong>Instrucciones</strong> <strong>de</strong> nomenclatura <strong>de</strong> eventosrecomendadas.• Cuando haga referencia a eventos en la documentación, utilice la frase, "se provocó un evento",en vez <strong>de</strong> "se activó un evento" o "se <strong>de</strong>senca<strong>de</strong>nó un evento".• En los lenguajes que admiten la palabra clave void, utilice el tipo <strong>de</strong> valor <strong>de</strong>vuelto <strong>de</strong> void <strong>para</strong>los controladores <strong>de</strong> eventos, como se muestra en el siguiente código en lenguaje C# <strong>de</strong>ejemplo.public <strong>de</strong>legate void MouseEventHandler(object sen<strong>de</strong>r, MouseEventArgs e);• Cuando un evento contenga datos importantes, tales como las coor<strong>de</strong>nadas <strong>de</strong> un clic <strong>de</strong>lmouse (ratón), use clases <strong>de</strong> datos <strong>de</strong> eventos con establecimiento inflexible <strong>de</strong> tipos.• Las clases <strong>de</strong> eventos <strong>de</strong>ben exten<strong>de</strong>r la Clase System.EventArgs, como se muestra en elejemplo siguiente.[Visual Basic]Public Class MouseEventArgsInherits EventArgs' Co<strong>de</strong> for the class goes here.End Class[C#]public class MouseEvent: EventArgs {}• Utilice un método virtual protected (Protected en Visual Basic) <strong>para</strong> provocar cada evento.Esta técnica no es a<strong>de</strong>cuada <strong>para</strong> clases sealed porque no permiten <strong>de</strong>rivar ninguna clase. Lafinalidad <strong>de</strong> este método es proporcionar a una clase <strong>de</strong>rivada una forma <strong>de</strong> controlar el eventomediante un reemplazo. Esto es más natural que utilizar <strong>de</strong>legados en las situaciones en lasque el programador crea una clase <strong>de</strong>rivada. El nombre <strong>de</strong>l método se convierte enOnEventName, don<strong>de</strong> EventName es el nombre <strong>de</strong>l evento provocado. Por ejemplo:[Visual Basic]Public Class ButtonPrivate onClickHandler As ButtonClickHandlerProtected Overridable Sub OnClick(e As ClickEventArgs)' Call the <strong>de</strong>legate if non-null.If Not (onClickHandler Is Nothing) ThenonClickHandler(Me, e)End IfEnd SubEnd Class
[C#]public class Button{}ButtonClickHandler onClickHandler;protected virtual void OnClick(ClickEventArgs e){ // Call the <strong>de</strong>legate if non-null.}if (onClickHandler != null)onClickHandler(this, e);La clase <strong>de</strong>rivada pue<strong>de</strong> elegir no llamar a la clase base durante el procesamiento <strong>de</strong>OnEventName. Teniendo en cuenta esto, no incluya ningún procesamiento en el métodoOnEventName que sea necesario <strong>para</strong> que la clase base funcione correctamente.• Hay que tener en cuenta que un controlador <strong>de</strong> eventos pue<strong>de</strong> contener cualquier código. Lasclases <strong>de</strong>ben estar pre<strong>para</strong>das <strong>para</strong> que el controlador <strong>de</strong> eventos realice cualquier operación y,en todos los casos, el objeto <strong>de</strong>be tener el estado a<strong>de</strong>cuado, una vez provocado el evento.Consi<strong>de</strong>re la utilización <strong>de</strong> un bloque try/finally en el punto <strong>de</strong>l código don<strong>de</strong> se provoca elevento. Como el programador pue<strong>de</strong> realizar una función <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada en el objeto<strong>para</strong> realizar otras acciones, no haga suposiciones sobre el estado <strong>de</strong>l objeto cuando el controlregrese al punto en el que provocó el evento. Por ejemplo:[Visual Basic]Public Class ButtonPrivate onClickHandler As ButtonClickHandlerProtected Sub DoClick()' Paint button in in<strong>de</strong>nted state.PaintDown()Try' Call event handler.OnClick()Finally' Window might be <strong>de</strong>leted in event handler.If Not (windowHandle Is Nothing) Then' Paint button in normal state.PaintUp()End IfEnd TryEnd SubProtected Overridable Sub OnClick(e As ClickEvent)
If Not (onClickHandler Is Nothing) ThenonClickHandler(Me, e)End IfEnd SubEnd Class[C#]public class Button{ ButtonClickHandler onClickHandler;}protected void DoClick(){ // Paint button in in<strong>de</strong>nted state.}PaintDown();try{ // Call event handler.}OnClick();finally{ // Window might be <strong>de</strong>leted in event handler.}if (windowHandle != null)// Paint button in normal state.PaintUp();protected virtual void OnClick(ClickEvent e){ if (onClickHandler != null)}onClickHandler(this, e);• Utilice o extienda la Clase System.ComponentMo<strong>de</strong>l.CancelEventArgs <strong>para</strong> permitir que elprogramador controle los eventos <strong>de</strong> un objeto. Por ejemplo, el control TreeView provocaBeforeLabelEdit cuando el usuario se dispone a editar una etiqueta <strong>de</strong> nodo. En el siguienteejemplo <strong>de</strong> código se muestra cómo pue<strong>de</strong> utilizar un programador este evento <strong>para</strong> evitar laedición <strong>de</strong> un nodo.[Visual Basic]Public Class Form1Inherits FormPrivate treeView1 As New TreeView()Sub treeView1_BeforeLabelEdit(source As Object, e As No<strong>de</strong>LabelEditEventArgs)
e.CancelEdit = TrueEnd SubEnd Class[C#]public class Form1: Form{}TreeView treeView1 = new TreeView();void treeView1_BeforeLabelEdit(object source,{}No<strong>de</strong>LabelEditEventArgs e)e.CancelEdit = true;Observe que en este caso, no se genera un error. Se trata <strong>de</strong> una etiqueta <strong>de</strong> sólo lectura.Cancelar eventos no resulta a<strong>de</strong>cuado en los casos en que el programador cancelaría laoperación y <strong>de</strong>volvería una excepción. En estos casos, se <strong>de</strong>be provocar una excepción <strong>de</strong>ntro<strong>de</strong>l controlador <strong>de</strong> eventos <strong>para</strong> cancelarlos. Por ejemplo, supongamos que el usuario <strong>de</strong>seaescribir lógica <strong>de</strong> validación en un control <strong>de</strong> edición como se muestra a continuación.[Visual Basic]Public Class Form1Inherits FormPrivate edit1 As EditBox = New EditBox()Sub TextChanging(source As Object, e As EventArgs)Throw New RuntimeException("Invalid edit")End SubEnd Class[C#]public class Form1: Form{ EditBox edit1 = new EditBox();void TextChanging(object source, EventArgs e){ throw new RuntimeException("Invalid edit"); }
<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> métodosEn las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> uso <strong>de</strong> métodos:• Elija un nombre <strong>para</strong> el evento siguiendo las <strong>Instrucciones</strong> <strong>de</strong> nomenclatura <strong>de</strong> eventos.• No utilice la notación húngara.• De forma pre<strong>de</strong>terminada, los métodos no son virtuales. Mantenga esta característicapre<strong>de</strong>terminada en las situaciones en las que no es necesario utilizar métodos virtuales. Paraobtener más información sobre la herencia <strong>de</strong> implementación, vea <strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong>clases base.<strong>Instrucciones</strong> <strong>de</strong> sobrecarga <strong>de</strong> métodosLa sobrecarga <strong>de</strong> métodos se produce cuando una clase contiene dos métodos con el mismonombre, pero firmas diferentes. En esta sección se proporcionan algunas instrucciones <strong>de</strong>utilización <strong>de</strong> métodos sobrecargados.• Utilice la sobrecarga <strong>de</strong> métodos <strong>para</strong> proporcionar métodos diferentes que semánticamentehagan lo mismo.• Utilice la sobrecarga <strong>de</strong> métodos, en vez <strong>de</strong> permitir argumentos pre<strong>de</strong>terminados. Losargumentos pre<strong>de</strong>terminados no controlan bien las versiones y, por tanto, no se permiten enCommon Language Specification (CLS). En el siguiente ejemplo <strong>de</strong> código se muestra unmétodo String.In<strong>de</strong>xOf sobrecargado.[Visual Basic]Function String.In<strong>de</strong>xOf(name As String) As IntegerFunction String.In<strong>de</strong>xOf(name As String, startIn<strong>de</strong>x As Integer) As Integer[C#]int String.In<strong>de</strong>xOf (String name);int String.In<strong>de</strong>xOf (String name, int startIn<strong>de</strong>x);• Utilice los valores pre<strong>de</strong>terminados correctamente. En una familia <strong>de</strong> métodos sobrecargados,el método complejo <strong>de</strong>be utilizar nombres <strong>de</strong> parámetros que indiquen un cambio <strong>de</strong>l estadopre<strong>de</strong>terminado que se supone en el método sencillo. Por ejemplo, en el código siguiente elprimer método supone que la búsqueda no hará distinción entre mayúsculas y minúsculas. Enel segundo método se utiliza el nombre ignoreCase en vez <strong>de</strong> caseSensitive <strong>para</strong> indicar cómose cambia el comportamiento pre<strong>de</strong>terminado.[Visual Basic]' Method #1: ignoreCase = false.Function Type.GetMethod(name As String) As MethodInfo' Method #2: Indicates how the <strong>de</strong>fault behavior of method #1' is being changed.Function Type.GetMethod(name As String, ignoreCase As Boolean) As MethodInfo[C#]// Method #1: ignoreCase = false.
MethodInfo Type.GetMethod(String name);// Method #2: Indicates how the <strong>de</strong>fault behavior of method #1 is being // changed.MethodInfo Type.GetMethod (String name, Boolean ignoreCase);• Utilice un mo<strong>de</strong>lo <strong>de</strong> nomenclatura y <strong>de</strong> or<strong>de</strong>n coherentes en los parámetros <strong>de</strong>l método. Lohabitual es proporcionar un conjunto <strong>de</strong> métodos sobrecargados con un número creciente <strong>de</strong>parámetros <strong>para</strong> que el programador pueda especificar el nivel <strong>de</strong> información <strong>de</strong>seado.Cuantos más parámetros especifique, más <strong>de</strong>talles pue<strong>de</strong> especificar el programador. En elsiguiente ejemplo <strong>de</strong> código, el método Execute contiene un or<strong>de</strong>n <strong>de</strong> parámetros coherente yuna variación <strong>de</strong>l mo<strong>de</strong>lo <strong>de</strong> nomenclatura. Cada variación <strong>de</strong>l método Execute utiliza la mismasemántica <strong>para</strong> el conjunto compartido <strong>de</strong> parámetros.[Visual Basic]Public Class SampleClassPrivate <strong>de</strong>faultForA As String = "<strong>de</strong>fault value for a"Private <strong>de</strong>faultForB As Integer = "42"Private <strong>de</strong>faultForC As Double = "68.90"Overloads Public Sub Execute()Execute(<strong>de</strong>faultForA, <strong>de</strong>faultForB, <strong>de</strong>faultForC)End SubOverloads Public Sub Execute(a As String)Execute(a, <strong>de</strong>faultForB, <strong>de</strong>faultForC)End SubOverloads Public Sub Execute(a As String, b As Integer)Execute(a, b, <strong>de</strong>faultForC)End SubOverloads Public Sub Execute(a As String, b As Integer, c As Double)Console.WriteLine(a)Console.WriteLine(b)Console.WriteLine(c)Console.WriteLine()End SubEnd Class[C#]public class SampleClass
{readonly string <strong>de</strong>faultForA = "<strong>de</strong>fault value for a";readonly int <strong>de</strong>faultForB = "42";readonly double <strong>de</strong>faultForC = "68.90";public void Execute(){ Execute(<strong>de</strong>faultForA, <strong>de</strong>faultForB, <strong>de</strong>faultForC); }public void Execute (string a){ Execute(a, <strong>de</strong>faultForB, <strong>de</strong>faultForC); }public void Execute (string a, int b){ Execute (a, b, <strong>de</strong>faultForC); }}public void Execute (string a, int b, double c){ Console.WriteLine(a);Console.WriteLine(b);Console.WriteLine(c);Console.WriteLine();}Tenga en cuenta que el único método <strong>de</strong>l grupo que <strong>de</strong>be ser virtual es el que más parámetrostiene y sólo cuando se precisa extensibilidad.• Si <strong>de</strong>be proporcionar la capacidad <strong>de</strong> reemplazar un método, establezca sólo la sobrecargamás completa como virtual y <strong>de</strong>fina las otras operaciones en función <strong>de</strong> esta sobrecarga. En elejemplo siguiente se muestra este mo<strong>de</strong>lo.[Visual Basic]Public Class SampleClassPrivate myString As StringPublic Sub New(str As String)Me.myString = strEnd SubOverloads Public Function In<strong>de</strong>xOf(s As String) As IntegerReturn In<strong>de</strong>xOf(s, 0)End FunctionOverloads Public Function In<strong>de</strong>xOf(s As String, startIn<strong>de</strong>x As
Integer) As IntegerReturn In<strong>de</strong>xOf(s, startIn<strong>de</strong>x, myString.Length - startIn<strong>de</strong>x)End FunctionOverloads Public Overridable Function In<strong>de</strong>xOf(s As String,startIn<strong>de</strong>x As Integer, count As Integer) As IntegerReturn myString.In<strong>de</strong>xOf(s, startIn<strong>de</strong>x, count)End FunctionEnd Class[C#]public class SampleClass{private string myString;public MyClass(string str){ this.myString = str; }public int In<strong>de</strong>xOf(string s){ return In<strong>de</strong>xOf (s, 0); }public int In<strong>de</strong>xOf(string s, int startIn<strong>de</strong>x){return In<strong>de</strong>xOf(s, startIn<strong>de</strong>x, myString.Length - startIn<strong>de</strong>x ); }}public virtual int In<strong>de</strong>xOf(string s, int startIn<strong>de</strong>x, int count){return myString.In<strong>de</strong>xOf(s, startIn<strong>de</strong>x, count); }Métodos con un número variable <strong>de</strong> argumentosPue<strong>de</strong> que <strong>de</strong>see exponer un método que incluya un número variable <strong>de</strong> argumentos. Un ejemploclásico es el método printf <strong>de</strong>l lenguaje <strong>de</strong> programación C. En las bibliotecas <strong>de</strong> clasesadministradas, utilice la palabra clave <strong>para</strong>ms (ParamArray en Visual Basic) <strong>para</strong> estaconstrucción. Por ejemplo, utilice el siguiente código en vez <strong>de</strong> varios métodos sobrecargados.[Visual Basic]Sub Format(formatString As String, ParamArray args() As Object)[C#]void Format(string formatString, <strong>para</strong>ms object [] args)No es conveniente utilizar únicamente la convención <strong>de</strong> llamada VarArgs o puntos suspensivos (...)ya que no es compatible con Common Language Specification.En un código sensible al rendimiento, pue<strong>de</strong> proporcionar rutas <strong>de</strong> acceso <strong>de</strong> código especiales<strong>para</strong> un reducido número <strong>de</strong> elementos. Sólo se <strong>de</strong>be hacer esto <strong>para</strong> la ruta <strong>de</strong> acceso a un código
completo <strong>de</strong> un caso especial (no únicamente crear una matriz y llamar al método general). Enestos casos, se recomienda utilizar el siguiente mo<strong>de</strong>lo <strong>para</strong> que haya un equilibrio entre elrendimiento y el costo <strong>de</strong>l código <strong>de</strong> un caso especial.[Visual Basic]Sub Format(formatString As String, arg1 As Object)Sub Format(formatString As String, arg1 As Object, arg2 As Object)Sub Format(formatString As String, ParamArray args() As Object)[C#]void Format(string formatString, object arg1)void Format(string formatString, object arg1, object arg2)void Format(string formatString, <strong>para</strong>ms object [] args)
<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> constructoresEn las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> uso <strong>de</strong> constructores:• Proporcione un constructor privado pre<strong>de</strong>terminado cuando sólo haya propieda<strong>de</strong>s y métodosestáticos en una clase. En el siguiente ejemplo, el constructor privado evita que se genere unaclase.[Visual Basic]NotInheritable Public Class Environment' Private constructor prevents the class from being created.Private Sub New()' Co<strong>de</strong> for the constructor goes here.End SubEnd Class[C#]public sealed class Environment{ // Private constructor prevents the class from being created.}private Environment(){ // Co<strong>de</strong> for the constructor goes here. }• Reduzca al mínimo la cantidad <strong>de</strong> trabajo realizado en el constructor. Los constructores no<strong>de</strong>berían hacer más que capturar el parámetro o parámetros <strong>de</strong> constructor. Esto retrasa elcosto que suponen más operaciones hasta que el usuario utilice una función específica <strong>de</strong> lainstancia.• Proporcione un constructor <strong>para</strong> cada clase. Si no se pue<strong>de</strong> crear un tipo, use un constructorprivado. Si no especifica un constructor, muchos lenguajes <strong>de</strong> programación (como C#) agreganimplícitamente un constructor público pre<strong>de</strong>terminado. Si la clase es abstracta, agrega unconstructor protegido.Tenga en cuenta que si agrega un constructor no pre<strong>de</strong>terminado a una clase en unacompilación posterior <strong>de</strong> la versión, el constructor pre<strong>de</strong>terminado implícito será eliminado, loque pue<strong>de</strong> dañar el código <strong>de</strong> cliente. Por lo tanto, lo mejor es especificar siempre <strong>de</strong> formaexplícita el constructor, aunque se trate <strong>de</strong> un constructor público pre<strong>de</strong>terminado.• Proporcione un constructor protected (Protected en Visual Basic) que se pueda utilizar en lostipos <strong>de</strong> una clase <strong>de</strong>rivada.• No proporcione constructores sin parámetros <strong>para</strong> los valores <strong>de</strong>l tipo struct. Tenga en cuentaque muchos compiladores no admiten que struct tenga un constructor sin parámetros. Si no seincluye un constructor, el tiempo <strong>de</strong> ejecución inicializa todos los campos <strong>de</strong> struct con un valorcero. Esto hace que la creación <strong>de</strong> campos estáticos y matrices sea más rápida.• Utilice los parámetros <strong>de</strong> los constructores como un método abreviado <strong>para</strong> establecerpropieda<strong>de</strong>s. No <strong>de</strong>be haber ninguna diferencia semántica entre un constructor vacío seguido
<strong>de</strong> <strong>de</strong>scriptores <strong>de</strong> acceso set y un constructor con múltiples argumentos. Los tres ejemplos <strong>de</strong>código siguiente son equivalentes:[Visual Basic]' Example #1.Dim SampleClass As New Class()SampleClass.A = "a"SampleClass.B = "b"' Example #2.Dim SampleClass As New Class("a")SampleClass.B = "b"' Example #3.Dim SampleClass As New Class("a", "b")[C#]// Example #1.Class SampleClass = new Class();SampleClass.A = "a";SampleClass.B = "b";// Example #2.Class SampleClass = new Class("a");SampleClass.B = "b";// Example #3.Class SampleClass = new Class ("a", "b");• Utilice un mo<strong>de</strong>lo <strong>de</strong> nomenclatura y <strong>de</strong> or<strong>de</strong>n coherente en los parámetros <strong>de</strong> constructores.Un mo<strong>de</strong>lo común <strong>de</strong> parámetros <strong>de</strong> constructores sirve <strong>para</strong> proporcionar un número <strong>de</strong>parámetros cada vez mayor que permita a los <strong>programadores</strong> especificar el nivel <strong>de</strong> información<strong>de</strong>seado. Cuantos más parámetros especifique, más <strong>de</strong>talles pue<strong>de</strong> especificar el programador.En el siguiente código <strong>de</strong> ejemplo, se muestra un mo<strong>de</strong>lo <strong>de</strong> or<strong>de</strong>n y nomenclatura <strong>de</strong>parámetros coherente <strong>para</strong> todos los constructores <strong>de</strong> SampleClass.[Visual Basic]Public Class SampleClassPrivate Const <strong>de</strong>faultForA As String = "<strong>de</strong>fault value for a"Private Const <strong>de</strong>faultForB As String = "<strong>de</strong>fault value for b"Private Const <strong>de</strong>faultForC As String = "<strong>de</strong>fault value for c"Public Sub New()MyClass.New(<strong>de</strong>faultForA, <strong>de</strong>faultForB, <strong>de</strong>faultForC)Console.WriteLine("New()")End Sub
Public Sub New(a As String)MyClass.New(a, <strong>de</strong>faultForB, <strong>de</strong>faultForC)End SubPublic Sub New(a As String, b As String)MyClass.New(a, b, <strong>de</strong>faultForC)End SubPublic Sub New(a As String, b As String, c As String)Me.a = aMe.b = bMe.c = cEnd SubEnd Class[C#]public class SampleClass{}private const string <strong>de</strong>faultForA = "<strong>de</strong>fault value for a";private const string <strong>de</strong>faultForB = "<strong>de</strong>fault value for b";private const string <strong>de</strong>faultForC = "<strong>de</strong>fault value for c";public MyClass():this(<strong>de</strong>faultForA, <strong>de</strong>faultForB, <strong>de</strong>faultForC) {}public MyClass (string a) : this(a, <strong>de</strong>faultForB, <strong>de</strong>faultForC) {}public MyClass (string a, string b) : this(a, b, <strong>de</strong>faultForC) {}public MyClass (string a, string b, string c)<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> camposEn las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> uso <strong>de</strong> campos:• No utilice campos <strong>de</strong> instancias que sean public o protected (Public o Protected en VisualBasic). Si evita exponer directamente los campos al programador, se pue<strong>de</strong>n tener versiones <strong>de</strong>las clases más fácilmente ya que no se pue<strong>de</strong> cambiar un campo en una propiedad y seguirmanteniendo la compatibilidad binaria. Consi<strong>de</strong>re la inclusión <strong>de</strong> los <strong>de</strong>scriptores <strong>de</strong> acceso <strong>de</strong>las propieda<strong>de</strong>s get y set en los campos, en vez <strong>de</strong> hacerlos públicos. La presencia <strong>de</strong> códigoejecutable en los <strong>de</strong>scriptores <strong>de</strong> acceso <strong>de</strong> las propieda<strong>de</strong>s get y set, permite introducirmejoras más a<strong>de</strong>lante, como crear un objeto a petición, cuando se utiliza la propiedad o en lanotificación <strong>de</strong> cambio <strong>de</strong> propiedad. En el siguiente ejemplo <strong>de</strong> código se muestra el usocorrecto <strong>de</strong> campos <strong>de</strong> instancia privados con los <strong>de</strong>scriptores <strong>de</strong> acceso <strong>de</strong> las propieda<strong>de</strong>sget y set.[Visual Basic]Public Structure Point
Private xValue As IntegerPrivate yValue As IntegerPublic Sub New(x As Integer, y As Integer)Me.xValue = xMe.yValue = yEnd SubPublic Property X() As IntegerGetReturn xValueEnd GetSetxValue = valueEnd SetEnd PropertyPublic Property Y() As IntegerGetReturn yValueEnd GetSetyValue = valueEnd SetEnd PropertyEnd Structure[C#]public struct Point{private int xValue;private int yValue;public Point(int x, int y){ this.xValue = x;this.yValue = y;}public int X{get
}{ return xValue; }set{ xValue = value; }}public int Y{get{ return yValue; }set{ yValue = value; }}• Exponga un campo a una clase <strong>de</strong>rivada utilizando una propiedad protected que <strong>de</strong>vuelve elvalor <strong>de</strong>l campo. Esto se muestra en el siguiente ejemplo <strong>de</strong> código:[Visual Basic]Public Class Control[C#]Inherits ComponentPrivate handle As IntegerProtected ReadOnly Property Handle() As IntegerGetReturn handleEnd GetEnd PropertyEnd Classpublic class Control: Component{}private int handle;protected int Handle{ get}{ return handle; }• Use la palabra clave const (Const en Visual Basic) <strong>para</strong> <strong>de</strong>clarar campos <strong>de</strong> constantes que novayan a cambiar. Los compiladores <strong>de</strong> lenguajes guardan los valores <strong>de</strong> los campos constdirectamente en código <strong>de</strong> llamada.
• Utilice campos estáticos públicos <strong>de</strong> sólo lectura <strong>para</strong> instancias <strong>de</strong> objetos pre<strong>de</strong>finidas. Si yahay instancias pre<strong>de</strong>finidas <strong>de</strong> un objeto, <strong>de</strong>clárelas como campos estáticos públicos <strong>de</strong> sólolectura <strong>de</strong>l objeto. Utilice el estilo <strong>de</strong> Mayúsculas y minúsculas Pascal pues son campospúblicos. En el siguiente ejemplo <strong>de</strong> código se muestra el uso correcto <strong>de</strong> campos estáticospúblicos <strong>de</strong> sólo lectura.[Visual Basic]Public Structure ColorPublic Shared Red As New Color(&HFF)Public Shared Green As New Color(&HFF00)Public Shared Blue As New Color(&HFF0000)Public Shared Black As New Color(&H0)Public Shared White As New Color(&HFFFFFF)Public Sub New(rgb As Integer)' Insert co<strong>de</strong> here.End SubPublic Sub New(r As Byte, g As Byte, b As Byte)' Insert co<strong>de</strong> here.End SubPublic ReadOnly Property RedValue() As ByteGetReturn ColorEnd GetEnd PropertyPublic ReadOnly Property GreenValue() As ByteGetReturn ColorEnd GetEnd PropertyPublic ReadOnly Property BlueValue() As ByteGetReturn ColorEnd GetEnd PropertyEnd Structure[C#]public struct Color{ public static readonly Color Red = new Color(0x0000FF);
}public static readonly Color Green = new Color(0x00FF00);public static readonly Color Blue = new Color(0xFF0000);public static readonly Color Black = new Color(0x000000);public static readonly Color White = new Color(0xFFFFFF);public Color(int rgb){ // Insert co<strong>de</strong> here.}public Color(byte r, byte g, byte b){ // Insert co<strong>de</strong> here.}public byte RedValue{ get { return Color; } }public byte GreenValue{ get { return Color; } }public byte BlueValue{ get { return Color; } }• Escriba al completo todas las palabras utilizadas en un nombre <strong>de</strong> campo. Utilice abreviaturassólo si los <strong>programadores</strong> en general las compren<strong>de</strong>n. No utilice mayúsculas en los nombres <strong>de</strong>campos. A continuación, se muestra un ejemplo <strong>de</strong> nombres <strong>de</strong> campos correctos.[Visual Basic]Class SampleClassPrivate url As StringPrivate <strong>de</strong>stinationUrl As StringEnd Class[C#]class SampleClass{ string url;string <strong>de</strong>stinationUrl; }• No utilice la notación húngara en nombres <strong>de</strong> campos. Los nombres correctos <strong>de</strong>scriben lasemántica y no el tipo.• No aplique un prefijo a nombres <strong>de</strong> campos o a nombres <strong>de</strong> campos estáticos. En concreto, noaplique un prefijo al nombre <strong>de</strong> un campo <strong>para</strong> diferenciar los campos estáticos <strong>de</strong> los camposno estáticos. Por ejemplo, aplicar un prefijo g_ o s_ es incorrecto.<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> parámetrosEn las reglas siguientes se <strong>de</strong>scriben las pautas <strong>de</strong> uso <strong>de</strong> parámetros:• Compruebe si los argumentos <strong>de</strong> los parámetros son válidos. Realice la validación <strong>de</strong>argumentos <strong>para</strong> cada método protegido o público y <strong>de</strong>scriptor <strong>de</strong> accesos <strong>de</strong> la propiedad set.Inicie excepciones significativas <strong>para</strong> el programador en los argumentos <strong>de</strong> parámetros no
válidos. Use la clase System.ArgumentException o una clase <strong>de</strong>rivada <strong>de</strong>System.ArgumentException. En el siguiente ejemplo se comprueba si los argumentos <strong>de</strong> losparámetros son válidos y se inician excepciones significativas.[Visual Basic]Class SampleClassPrivate countValue As IntegerPrivate maxValue As Integer = 100Public Property Count() As IntegerGetReturn countValueEnd GetSet' Check for valid <strong>para</strong>meter.If value < 0 Or value >= maxValue ThenThrow New ArgumentOutOfRangeException("value", value,"Value is invalid.")End IfcountValue = valueEnd SetEnd PropertyPublic Sub SelectItem(start As Integer, [end] As Integer)' Check for valid <strong>para</strong>meter.If start < 0 ThenThrow New ArgumentOutOfRangeException("start", start, "Startis invalid.")End If' Check for valid <strong>para</strong>meter.If [end] < start ThenThrow New ArgumentOutOfRangeException("end", [end], "End isinvalid.")End If' Insert co<strong>de</strong> to do other work here.Console.WriteLine("Starting at {0}", start)Console.WriteLine("Ending at {0}", [end])End Sub
End Class[C#]class SampleClass{}public int Count{ get}{ return count; }set{ // Check for valid <strong>para</strong>meter.}if (count < 0 || count >= MaxValue)throw newArgumentOutOfRangeException(Sys.GetString("InvalidArgument","value",count.ToString()));public void Select(int start, int end){}// Check for valid <strong>para</strong>meter.if (start < 0)throw new ArgumentException(Sys.GetString("InvalidArgument","start",start.ToString()));// Check for valid <strong>para</strong>meter.if (end < start)throw new ArgumentException(Sys.GetString("InvalidArgument","end",end.ToString()));Tenga en cuenta que la comprobación real no tiene que producirse necesariamente en losmétodos public o protected. Se pue<strong>de</strong> producir en un nivel inferior <strong>de</strong> rutinas privadas. Lacuestión principal es que en todo el área <strong>de</strong> la superficie que se expone al programador secomprueba si los argumentos son válidos.• Asegúrese <strong>de</strong> compren<strong>de</strong>r totalmente las implicaciones <strong>de</strong> pasar parámetros por valor o porreferencia. Pasar un parámetro por valor copia el valor que se está pasando y no produceningún efecto en el valor original. En el siguiente ejemplo <strong>de</strong> método se pasan parámetros porvalor.public void Add(object value){}
Pasar un parámetro por referencia pasa la ubicación <strong>de</strong> almacenamiento <strong>de</strong>l valor. Comoresultado, se pue<strong>de</strong>n realizar cambios en el valor <strong>de</strong>l parámetro. En el siguiente ejemplo <strong>de</strong>método se pasa un parámetro por referencia.public static int Exchange(ref int location, int value){}Un parámetro <strong>de</strong> salida representa la misma ubicación <strong>de</strong> almacenamiento que la variableespecificada como argumento en la invocación <strong>de</strong>l método. Como resultado, sólo se pue<strong>de</strong>nrealizar cambios en el parámetro <strong>de</strong> salida. En el siguiente ejemplo <strong>de</strong> método se pasa unparámetro <strong>de</strong> salida.[DllImport("Kernel32.dll"]public static extern bool QueryPerformanceCounter(out long value)
<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> tiposLos tipos constituyen las unida<strong>de</strong>s <strong>de</strong> encapsulación en Common Language Runtime. Para obteneruna <strong>de</strong>scripción <strong>de</strong>tallada <strong>de</strong> la lista completa <strong>de</strong> tipos <strong>de</strong> datos compatibles con el motor <strong>de</strong> tiempo<strong>de</strong> ejecución, vea Sistema <strong>de</strong> tipos común. En esta sección se proporcionan algunas instrucciones<strong>de</strong> utilización <strong>de</strong> clases básicas <strong>de</strong> tipos.<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> clases baseUna clase es uno <strong>de</strong> los tipos más común. Las clases pue<strong>de</strong>n ser abstractas o selladas. Una claseabstracta requiere una clase <strong>de</strong>rivada <strong>para</strong> proporcionar una implementación. Una clase sealed noadmite una clase <strong>de</strong>rivada. Es conveniente utilizar clases, en vez <strong>de</strong> otros tipos.Las clases base son una forma útil <strong>de</strong> agrupar objetos que comparten un conjunto común <strong>de</strong>funcionalida<strong>de</strong>s. Las clases base pue<strong>de</strong>n proporcionar un conjunto pre<strong>de</strong>terminado <strong>de</strong>funcionalida<strong>de</strong>s a la vez que permiten la personalización mediante la ampliación.Se <strong>de</strong>bería proporcionar un constructor <strong>de</strong> forma explícita <strong>para</strong> todas las clases, ya que,normalmente, los compiladores agregan un constructor público pre<strong>de</strong>terminado a las clases que no<strong>de</strong>finen un constructor. Si la intención es que la clase no se pueda crear, el hecho <strong>de</strong> que contengadicho constructor público pre<strong>de</strong>terminado pue<strong>de</strong> resultar confuso <strong>para</strong> los usuarios <strong>de</strong> ésta. Por lotanto, lo mejor es <strong>de</strong>finir siempre al menos un constructor por clase. Si no se <strong>de</strong>sea que ésta sepueda crear, se pue<strong>de</strong> configurar el constructor como privado.Debe agregar extensibilidad o polimorfismo al diseño sólo si tiene un escenario <strong>de</strong>l cliente claro. Porejemplo, proporcionar una interfaz <strong>para</strong> adaptadores <strong>de</strong> datos es complicado y no ofrece beneficiosreales. A<strong>de</strong>más, los <strong>programadores</strong> <strong>de</strong>berán programar específicamente cada adaptador, por tanto,el beneficio que proporciona una interfaz es mínimo. Sin embargo, no es necesario que seancoherentes entre todos los adaptadores. Aunque una interfaz o clase abstracta no es lo apropiadoen esta situación, es muy importante proporcionar un mo<strong>de</strong>lo coherente. Se pue<strong>de</strong>n proporcionarmo<strong>de</strong>los coherentes <strong>para</strong> <strong>programadores</strong> en las clases base. Siga estas instrucciones <strong>para</strong> crearclases base.Clases base e interfacesUn tipo <strong>de</strong> interfaz es una especificación <strong>de</strong> un protocolo, potencialmente compatible con muchostipos <strong>de</strong> objetos. Utilice clases base en vez <strong>de</strong> interfaces siempre que sea posible. Des<strong>de</strong> laperspectiva <strong>de</strong> las versiones, las clases son más flexibles que las interfaces. Con una clase, sepue<strong>de</strong> incluir la versión 1.0 y, a continuación, en la versión 2.0 agregar un nuevo método a la clase.Siempre que el método no sea abstracto, las clases <strong>de</strong>rivadas existentes seguirán funcionando sinsufrir ningún cambio.Como las interfaces no son compatibles con la herencia <strong>de</strong> implementación, el mo<strong>de</strong>lo aplicado alas clases no se aplica a las interfaces. Agregar un método a una interfaz es lo mismo que agregarun método abstracto a una clase base; cualquier clase que implemente la interfaz interrumpirá laejecución porque la clase no implementa el nuevo método.La utilización <strong>de</strong> interfaces es a<strong>de</strong>cuada en las situaciones siguientes:• Hay varias clases no relacionadas que son compatibles con el protocolo.• Estas clases ya han establecido clases base (por ejemplo, algunas son controles <strong>de</strong> interfaz <strong>de</strong>usuario (UI) y otras son servicios Web XML).
• La agregación no es a<strong>de</strong>cuada o no es viable.En el resto <strong>de</strong> las situaciones, es mejor utilizar el mo<strong>de</strong>lo <strong>de</strong> herencia <strong>de</strong> clases.Constructores y métodos protegidosProporcione la personalización <strong>de</strong> clases a través <strong>de</strong> métodos protegidos. La interfaz pública <strong>de</strong> unaclase base <strong>de</strong>be proporcionar un conjunto completo <strong>de</strong> funcionalida<strong>de</strong>s <strong>para</strong> el consumidor <strong>de</strong> laclase. Sin embargo, los usuarios <strong>de</strong> la clase a menudo <strong>de</strong>sean implementar el menor número <strong>de</strong>métodos posible y, <strong>de</strong> este modo, proporcionar el conjunto completo <strong>de</strong> funcionalida<strong>de</strong>s alconsumidor. Con esta finalidad, proporcione un conjunto <strong>de</strong> métodos públicos finales o no virtualesque llaman a través <strong>de</strong> un único método protegido que proporciona implementaciones <strong>para</strong> losmétodos. Este método se <strong>de</strong>be marcar con el sufijo Impl. La utilización <strong>de</strong> este mo<strong>de</strong>lo tambiénproporciona lo que se conoce como método Template. En el siguiente ejemplo <strong>de</strong> código semuestra este proceso.[Visual Basic]Public Class SampleClassPrivate x As IntegerPrivate y As IntegerPrivate width As IntegerPrivate height As IntegerPrivate specified As BoundsSpecifiedOverloads Public Sub SetBounds(x As Integer, y As Integer, width AsInteger, height As Integer)SetBoundsImpl(x, y, width, height, Me.specified)End SubOverloads Public Sub SetBounds(x As Integer, y As Integer, width AsInteger, height As Integer, specified As BoundsSpecified)SetBoundsImpl(x, y, width, height, specified)End SubProtected Overridable Sub SetBoundsImpl(x As Integer, y As Integer,width As Integer, height As Integer,specified As BoundsSpecified)' Insert co<strong>de</strong> to perform meaningful operations here.Me.x = xMe.y = yMe.width = widthMe.height = heightMe.specified = specifiedConsole.WriteLine("x {0}, y {1}, width {2}, height {3}, bounds {4}",Me.x, Me.y, Me.width, Me.height, Me.specified)
End SubEnd Class[C#]public class MyClass{private int x;private int y;private int width;private int height;BoundsSpecified specified;public void SetBounds(int x, int y, int width, int height){ SetBoundsImpl(x, y, width, height, this.specified); }public void SetBounds(int x, int y, int width, int height,BoundsSpecified specified){ SetBoundsImpl(x, y, width, height, specified); }}protected virtual void SetBoundsImpl(int x, int y, int width, intheight, BoundsSpecified specified){ // Add co<strong>de</strong> to perform meaningful opertions here.this.x = x;this.y = y;this.width = width;this.height = height;this.specified = specified;}Si no incluye un constructor public o protected, la mayoría <strong>de</strong> los compiladores, como el <strong>de</strong> C#,insertarán uno. Por tanto, <strong>para</strong> una mejor documentación y legibilidad <strong>de</strong>l código fuente, <strong>de</strong>berá<strong>de</strong>finir explícitamente un constructor protected en todas las clases abstractas.<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> clases sealedUtilice clases sealed si sólo hay propieda<strong>de</strong>s y métodos estáticos en una clase.<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> tipos <strong>de</strong> valorUn tipo <strong>de</strong> valor <strong>de</strong>scribe un valor que se representa como una secuencia <strong>de</strong> bits almacenada en lapila. Para obtener una <strong>de</strong>scripción <strong>de</strong> todos los tipos <strong>de</strong> datos integrados <strong>de</strong> .NET Framework, vea
Tipos <strong>de</strong> valor. En esta sección se proporcionan las instrucciones <strong>de</strong> uso <strong>de</strong> tipos <strong>de</strong> valor <strong>de</strong>enumeraciones (enum) y <strong>de</strong> estructuras (struct).<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> estructurasSe recomienda utilizar un valor struct <strong>para</strong> los tipos que cumplan los siguientes criterios:• Actúan como tipos primitivos.• Tienen un tamaño <strong>de</strong> instancia inferior a 16 bytes.• Son inmutables.• Es conveniente utilizar la semántica <strong>de</strong> valor.En el siguiente ejemplo se muestra una estructura <strong>de</strong>finida correctamente.[Visual Basic]Public Structure Int32Implements IFormattableImplements ICom<strong>para</strong>blePublic Const MinValue As Integer = -2147483648Public Const MaxValue As Integer = 214748364Private intValue As IntegeOverloads Public Shared Function ToString(i As Integer) As String' Insert co<strong>de</strong> here.End FunctionOverloads Public Function ToString(ByVal format As String, ByValformatProvi<strong>de</strong>r As IFormatProvi<strong>de</strong>r) As String ImplementsIFormattable.ToString' Insert co<strong>de</strong> here.End FunctionOverloads Public Overri<strong>de</strong>s Function ToString() As String' Insert co<strong>de</strong> here.End FunctionPublic Shared Function Parse(s As String) As Integer' Insert co<strong>de</strong> here.Return 0End FunctionPublic Overri<strong>de</strong>s Function GetHashCo<strong>de</strong>() As Integer' Insert co<strong>de</strong> here.Return 0End Function
Public Overri<strong>de</strong>s Overloads Function Equals(obj As Object) As Boolean' Insert co<strong>de</strong> here.Return FalseEnd FunctionPublic Function CompareTo(obj As Object) As Integer ImplementsICom<strong>para</strong>ble.CompareTo' Insert co<strong>de</strong> here.Return 0End FunctionEnd Structure[C#]public struct Int32: ICom<strong>para</strong>ble, IFormattable{public const int MinValue = -2147483648;public const int MaxValue = 2147483647;public static string ToString(int i){ // Insert co<strong>de</strong> here. }public string ToString(string format, IFormatProvi<strong>de</strong>r formatProvi<strong>de</strong>r){ // Insert co<strong>de</strong> here. }public overri<strong>de</strong> string ToString(){ // Insert co<strong>de</strong> here. }public static int Parse(string s){ // Insert co<strong>de</strong> here.return 0; }public overri<strong>de</strong> int GetHashCo<strong>de</strong>(){ // Insert co<strong>de</strong> here.return 0; }public overri<strong>de</strong> bool Equals(object obj){ // Insert co<strong>de</strong> here.return false; }
}public int CompareTo(object obj){ // Insert co<strong>de</strong> here.return 0; }• No incluya un constructor pre<strong>de</strong>terminado <strong>para</strong> struct. Tenga en cuenta que C# no admite questruct tenga un constructor pre<strong>de</strong>terminado. El motor en tiempo <strong>de</strong> ejecución inserta unconstructor que inicializa todos los valores en un estado cero. Esto permite crear matrices <strong>de</strong>estructuras sin ejecutar el constructor en cada instancia. No haga que una estructura struct<strong>de</strong>penda <strong>de</strong> un constructor que se invoque <strong>para</strong> cada instancia. Las instancias <strong>de</strong> estructuras sepue<strong>de</strong>n crear con un valor <strong>de</strong> cero sin ejecutar un constructor. También se <strong>de</strong>be diseñar unaestructura struct <strong>para</strong> un estado en el que todos los datos <strong>de</strong> instancias se establezcan encero, false o null (según corresponda) <strong>para</strong> que sean válidos.<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> enumeracionesEn las reglas siguientes se <strong>de</strong>scriben las pautas <strong>de</strong> uso <strong>de</strong> enumeraciones:• No utilice el sufijo Enum en tipos enum.• Utilice un tipo <strong>de</strong> valor enum <strong>para</strong> escribir tipos <strong>de</strong>vueltos, parámetros, propieda<strong>de</strong>s seguros.Defina siempre los valores enumerados que se utilizan en parámetros y propieda<strong>de</strong>s medianteel tipo enum. De este modo, las herramientas <strong>de</strong> <strong>de</strong>sarrollo conocen los valores posibles <strong>de</strong>una propiedad o parámetro. En el ejemplo siguiente se muestra cómo <strong>de</strong>finir un tipo enum.[Visual Basic]Public Enum FileMo<strong>de</strong>AppendCreateCreateNewOpenOpenOrCreateTruncateEnd Enum
[C#]public enum FileMo<strong>de</strong>{ Append,}Create,CreateNew,Open,OpenOrCreate,TruncateEn el ejemplo siguiente se muestra el constructor <strong>de</strong> un objeto FileStream que utiliza laenumeración FileMo<strong>de</strong>.[Visual Basic]Public Sub New(ByVal path As String, ByVal mo<strong>de</strong> As FileMo<strong>de</strong>);[C#]public FileStream(string path, FileMo<strong>de</strong> mo<strong>de</strong>);• Utilice un tipo <strong>de</strong> valor enum en vez <strong>de</strong> constantes estáticas finales.• No utilice un tipo <strong>de</strong> valor enum en conjuntos abiertos (como la versión <strong>de</strong>l sistema operativo).• Utilice la Clase System.FlagsAttribute <strong>para</strong> crear atributos personalizados <strong>para</strong> un tipo <strong>de</strong> valorenum sólo si se realiza una operación OR bit a bit en valores numéricos. Use potencias <strong>de</strong> dos<strong>para</strong> los valores <strong>de</strong> enum <strong>de</strong> forma que puedan combinarse con facilidad. Este atributo seaplica en el siguiente ejemplo <strong>de</strong> código.[Visual Basic]Public Enum WatcherChangeTypesCreated = 1Deleted = 2Changed = 4Renamed = 8All = Created Or Deleted Or Changed Or RenamedEnd Enum[C#][Flags()]public enum WatcherChangeTypes{ Created = 1,Deleted = 2,Changed = 4,Renamed = 8,
};All = Created | Deleted | Changed | RenamedNota Una excepción a esta regla es la encapsulación <strong>de</strong> una API <strong>de</strong> Win32. Normalmente,se utilizan <strong>de</strong>finiciones internas que vienen en un encabezado Win32. Se pue<strong>de</strong>n <strong>de</strong>jarestas <strong>de</strong>finiciones con las mayúsculas y minúsculas <strong>de</strong> Win32 que, por lo general, estántodas las letras en mayúsculas.• Consi<strong>de</strong>re proporcionar constantes con nombre <strong>para</strong> las combinaciones <strong>de</strong> indicadores usadascon frecuencia. Usar una operación OR bit a bit es un concepto avanzado y no <strong>de</strong>beríarequerirse <strong>para</strong> tareas sencillas. Esto se muestra en el siguiente ejemplo <strong>de</strong> enumeración:[Visual Basic] _Public Enum FileAccessRead = 1Write = 2ReadWrite = Read Or WriteEnd Enum[C#][Flags()]public enum FileAccess{}Read = 1,Write = 2,ReadWrite = Read | Write,• Utilice el tipo Int32 como el tipo subyacente <strong>de</strong> un tipo <strong>de</strong> valor enum salvo que se cumpla una<strong>de</strong> las siguiente condiciones:• El tipo <strong>de</strong> valor enum representa indicadores y hay ya más <strong>de</strong> 32 indicadores; o el tipo <strong>de</strong>valor enum podría tener muchos indicadores en el futuro.• El tipo <strong>de</strong>be ser diferente <strong>de</strong> int <strong>para</strong> que sea compatible con versiones anteriores.• Tenga en cuenta que los argumentos enum no siempre se encuentran en el intervalo <strong>de</strong>finido.Resulta válido convertir cualquier valor entero al tipo enum aunque el valor no esté <strong>de</strong>finido enel tipo enum. Realice una validación <strong>de</strong> argumentos cómo se muestra en el siguiente ejemplo<strong>de</strong> código.[Visual Basic]Public Sub SetColor(newColor As Color)If Not [Enum].IsDefined(GetType(Color), newColor) ThenThrow New ArgumentOutOfRangeException()End If
End Sub[C#]public void SetColor (Color color){ if (!Enum.IsDefined (typeof(Color), color)}throw new ArgumentOutOfRangeException();<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> <strong>de</strong>legadosLos <strong>de</strong>legados son una herramienta eficaz que permite al diseñador <strong>de</strong> mo<strong>de</strong>los <strong>de</strong> objetos <strong>de</strong>código administrado encapsular llamadas a métodos. Los <strong>de</strong>legados son muy útiles en lasnotificaciones <strong>de</strong> eventos y en las funciones <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada.Notificaciones <strong>de</strong> eventosUtilice el mo<strong>de</strong>lo <strong>de</strong> diseño <strong>de</strong> eventos a<strong>de</strong>cuado aunque el evento no esté relacionado con lainterfaz <strong>de</strong> usuario. Para obtener más información sobre la utilización <strong>de</strong> eventos, vea <strong>Instrucciones</strong><strong>de</strong> uso <strong>de</strong> eventos.Funciones <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamadaLas funciones <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada se pasan a un método <strong>para</strong> que se pueda llamar al código<strong>de</strong> usuario varias veces durante la ejecución <strong>para</strong> proporcionar personalización. Pasar una función<strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada Compare a una rutina <strong>de</strong> or<strong>de</strong>nación es un ejemplo clásico <strong>de</strong> utilización<strong>de</strong> estas funciones. Estos métodos <strong>de</strong>ben utilizar las convenciones <strong>de</strong> las funciones <strong>de</strong> <strong>de</strong>volución<strong>de</strong> llamada <strong>de</strong>scritas en la sección Uso <strong>de</strong> las funciones <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada.Termine los nombres <strong>de</strong> funciones <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada con el sufijo Callback.<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> atributos.NET Framework permite a los <strong>programadores</strong> inventar nuevas clases <strong>de</strong> información <strong>de</strong>clarativa,especificar información <strong>de</strong>clarativa <strong>para</strong> entida<strong>de</strong>s <strong>de</strong> programas diversos y recuperar la informaciónsobre atributos en el entorno <strong>de</strong>l motor <strong>de</strong> tiempo <strong>de</strong> ejecución. Por ejemplo, en un marco <strong>de</strong> trabajose pue<strong>de</strong> <strong>de</strong>finir un atributo HelpAttribute, que se pue<strong>de</strong> colocar en elementos <strong>de</strong> programa comoclases y métodos, <strong>para</strong> proporcionar una asignación <strong>de</strong> los elementos <strong>de</strong>l programa a ladocumentación correspondiente. En la <strong>de</strong>claración <strong>de</strong> clases <strong>de</strong> atributos se pue<strong>de</strong>n <strong>de</strong>finir nuevasclases <strong>de</strong> información <strong>de</strong>clarativa, que pue<strong>de</strong>n tener parámetros con nombres y parámetrosposicionales. Para obtener más información, vea Escribir atributos personalizados.En las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> uso <strong>de</strong> las clases <strong>de</strong> atributos:• Agregue el sufijo Attribute a clases <strong>de</strong> atributos personalizados, como se muestra en elsiguiente ejemplo.[Visual Basic]Public Class ObsoleteAttribute{}[C#]public class ObsoleteAttribute{}
• Especifique AttributeUsage en los atributos <strong>para</strong> <strong>de</strong>finir su utilización exacta, como se muestraen el siguiente ejemplo.[Visual Basic] _Public Class ObsoleteAttributeInherits Attribute' Insert co<strong>de</strong> here.End Class[C#][AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]public class ObsoleteAttribute: Attribute {}• Selle las clases <strong>de</strong> atributos siempre que sea posible, <strong>para</strong> que no se pueda <strong>de</strong>rivar ningunaclase.• Utilice argumentos posicionales (parámetros <strong>de</strong>l constructor) en los parámetros requeridos.Proporcione una propiedad <strong>de</strong> sólo lectura con el mismo nombre que el argumento posicional,pero cambie el uso <strong>de</strong> las mayúsculas y minúsculas <strong>para</strong> diferenciarlos. Esto permite el accesoal argumento en tiempo <strong>de</strong> ejecución.• Utilice argumentos con nombres (propieda<strong>de</strong>s <strong>de</strong> lectura y escritura) en los parámetrosopcionales. Proporcione una propiedad <strong>de</strong> lectura/escritura con el mismo nombre que elargumento con nombre, pero cambie el uso <strong>de</strong> las mayúsculas y minúsculas <strong>para</strong> diferenciarlos.• No <strong>de</strong>fina un parámetro con argumentos posicionales y argumentos con nombre. En el ejemplosiguiente se muestra este mo<strong>de</strong>lo.[Visual Basic]Public Class NameAttributeInherits Attribute' This is a positional argument.Public Sub New(username As String)' Implement co<strong>de</strong> here.End SubPublic ReadOnly Property UserName() As StringGetReturn UserNameEnd GetEnd Property' This is a named argument.Public Property Age() As IntegerGetReturn Age
End GetSetAge = valueEnd SetEnd PropertyEnd Class[C#]public class NameAttribute: Attribute{ // This is a positional argument.}public NameAttribute (string username){ // Implement co<strong>de</strong> here. }public string UserName{ get}{ return UserName; }// This is a named argument.public int Age{ get}{ return Age; }set{ Age = value; }<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> tipos anidadosLos tipos anidados son tipos <strong>de</strong>finidos <strong>de</strong>ntro <strong>de</strong>l ámbito <strong>de</strong> otro tipo. Los tipos anidados resultanmuy útiles <strong>para</strong> encapsular los <strong>de</strong>talles <strong>de</strong> implementación <strong>de</strong> un tipo, como un enumerador en unacolección, ya que pue<strong>de</strong>n tener acceso al estado privado.Los tipos anidados públicos se <strong>de</strong>ben utilizar en contadas ocasiones. Utilícelos sólo en situacionesen las que se cumplan las condiciones siguientes:• El tipo anidado (tipo interno) pertenece <strong>de</strong> forma lógica al tipo contenedor (tipo externo).En los ejemplos siguientes se muestra cómo <strong>de</strong>finir tipos con y sin tipos anidados:[Visual Basic]' With nested types.ListBox.SelectedObjectCollection' Without nested types.ListBoxSelectedObjectCollection
' With nested types.RichTextBox.ScrollBars' Without nested types.RichTextBoxScrollBars[C#]// With nested types.ListBox.SelectedObjectCollection// Without nested types.ListBoxSelectedObjectCollection// With nested types.RichTextBox.ScrollBars// Without nested types.RichTextBoxScrollBarsNo utilice tipos anidados si se cumplen las siguientes condiciones:• Las instancias <strong>de</strong>l tipo <strong>de</strong>ben crearse mediante el código <strong>de</strong> cliente. Si un tipo tiene unconstructor público, probablemente no <strong>de</strong>bería anidarse. La razón tras esta directriz es que si sepue<strong>de</strong>n crear instancias <strong>de</strong> un tipo anidado, esto indicaría que el tipo ocupa un lugar en supropia biblioteca. Se pue<strong>de</strong> crear, usar y <strong>de</strong>struir sin usar el tipo externo; por lo tanto, no<strong>de</strong>bería estar anidado. Un tipo interno no se <strong>de</strong>bería reutilizar ampliamente fuera <strong>de</strong>l tipoexterno sin una relación con el tipo externo.• Las referencias al tipo se <strong>de</strong>claran normalmente en el código <strong>de</strong> cliente.<strong>Instrucciones</strong> <strong>para</strong> exponer funcionalidad a COMCommon Language Runtime proporciona compatibilidad total <strong>para</strong> interoperar con componentesCOM. Un componente COM se pue<strong>de</strong> utilizar <strong>de</strong>ntro <strong>de</strong> un tipo administrado y una instanciaadministrada se pue<strong>de</strong> utilizar en un componente COM. Esta compatibilidad es la clave <strong>para</strong><strong>de</strong>splazar una parte <strong>de</strong>l código no administrado al código administrado cada vez; sin embargo,también presenta algunos problemas <strong>para</strong> los diseñadores <strong>de</strong> bibliotecas <strong>de</strong> clases. Para exponertotalmente un tipo administrado a clientes COM, el tipo <strong>de</strong>be exponer funcionalidad <strong>de</strong> forma quesea compatible y respete los términos <strong>de</strong>l contrato <strong>de</strong> control <strong>de</strong> versiones <strong>de</strong> COM.Marque las bibliotecas <strong>de</strong> clases administradas con el atributo ComVisibleAttribute <strong>para</strong> indicar silos clientes COM pue<strong>de</strong>n utilizar la biblioteca directamente o <strong>de</strong>ben utilizar un contenedor <strong>para</strong>adaptar las funcionalida<strong>de</strong>s.Los tipos y las interfaces que se <strong>de</strong>ben utilizar directamente en los clientes COM, como alojarlos enun contenedor no administrado, se <strong>de</strong>ben marcar con el atributo ComVisible(true). El cierretransitivo <strong>de</strong> todos los tipos a los que se hace referencia en los tipos expuestos <strong>de</strong>be marcarseexplícitamente como ComVisible(true); en caso contrario, se expondrán como IUnknown.Nota Los miembros <strong>de</strong> un tipo también <strong>de</strong>ben marcarse como ComVisible(false); esto reducela exposición a COM y, por tanto, las restricciones en la utilización <strong>de</strong> tipos administrados.
Los tipos marcados con el atributo ComVisible(true) no pue<strong>de</strong>n exponer las funcionalida<strong>de</strong>súnicamente <strong>de</strong> forma que no se puedan utilizar en COM. En concreto, COM no es compatible conlos métodos estáticos ni con los constructores <strong>para</strong>metrizados. Pruebe la funcionalidad <strong>de</strong>l tipo enclientes COM <strong>para</strong> comprobar que su comportamiento es correcto. Asegúrese <strong>de</strong> que compren<strong>de</strong> elimpacto que tiene en el Registro hacer que todos los tipos se puedan crear conjuntamente.Cálculo por referenciaLos objetos <strong>de</strong> cálculo por referencia son Objetos utilizables <strong>de</strong> forma remota. La interacción remota<strong>de</strong> objetos se aplica a las tres clases <strong>de</strong> tipos siguientes:• Tipos cuyas instancias se copian cuando se calculan sus referencias a través <strong>de</strong> un límiteAppDomain (en el mismo equipo o en un equipo diferente). Estos tipos se <strong>de</strong>ben marcar con elatributo Serializable.• Tipos <strong>para</strong> los que el motor <strong>de</strong> ejecución crea un proxy transparente cuando se calculan susreferencias a través <strong>de</strong> un límite AppDomain (en el mismo equipo o en un equipo diferente).Estos tipos se <strong>de</strong>ben <strong>de</strong>rivar en última instancia <strong>de</strong> la Clase System.MarshalByRefObject.• Tipos cuyas referencias no se calculan en ningún caso a través <strong>de</strong> dominios AppDomains. Éstees el valor pre<strong>de</strong>terminado.Siga estas instrucciones al utilizar el cálculo <strong>de</strong> referencias por referencia:• De forma pre<strong>de</strong>terminada, las referencias <strong>de</strong> instancias se <strong>de</strong>ben calcular con objetos <strong>de</strong>valores. Esto significa que sus tipos se <strong>de</strong>ben marcar como Serializable.• Las referencias <strong>de</strong> tipos <strong>de</strong> componentes se <strong>de</strong>ben calcular con objetos <strong>de</strong> referencia. Éste ya<strong>de</strong>bería ser el caso <strong>de</strong> la mayoría <strong>de</strong> componentes, porque la clase base común o ClaseSystem.Component, es una clase <strong>de</strong> cálculo por referencia.• Si el tipo encapsula un recurso <strong>de</strong>l sistema operativo, las referencias <strong>de</strong>l recurso se <strong>de</strong>bencalcular con un objeto <strong>de</strong> referencia. Si el tipo implementa la Interfaz IDisposable es muyprobable que las referencias se calculen por referencia. System.IO.Stream se <strong>de</strong>riva <strong>de</strong>MarshalByRefObject. En la mayoría <strong>de</strong> las secuencias, como FileStreams y <strong>Net</strong>workStreams, seencapsulan recursos externos <strong>para</strong> que las referencias se calculen con objetos <strong>de</strong> referencia.• En las instancias que simplemente alojan un estado las referencias se <strong>de</strong>ben calcular conobjetos <strong>de</strong> valor (como DataSet).• Los tipos especiales que no se pue<strong>de</strong>n llamar a través <strong>de</strong> un límite AppDomain (como elpropietario <strong>de</strong> métodos estáticos <strong>de</strong> utilida<strong>de</strong>s) no se <strong>de</strong>ben marcar como Serializable.
<strong>Instrucciones</strong> <strong>para</strong> provocar y controlar erroresEn las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>para</strong> provocar y controlar errores:• Todas las rutas <strong>de</strong> código que generan una excepción <strong>de</strong>ben proporcionar un método <strong>para</strong>comprobar el funcionamiento correcto sin que se inicie una excepción. Por ejemplo, <strong>para</strong> evitaruna excepción FileNotFoundException, se pue<strong>de</strong> invocar File.Exists; aunque esto pue<strong>de</strong> noser siempre posible, pero el objetivo consiste en que no se inicien excepciones en condiciones<strong>de</strong> ejecución normales.• Termine los nombres <strong>de</strong> la clase Exception con el sufijo Exception como en el siguienteejemplo.[Visual Basic]Public Class FileNotFoundExceptionInherits Exception' Implementation co<strong>de</strong> goes here.End Class[C#]public class FileNotFoundException : Exception{ // Implementation co<strong>de</strong> goes here.}• Al crear clases <strong>de</strong> excepciones, use los constructores comunes que se muestran en el siguienteejemplo <strong>de</strong> código.[Visual Basic]Public Class XxxExceptionInherits ApplicationExceptionPublic Sub New()' Implementation co<strong>de</strong> goes here.End SubPublic Sub New(message As String)' Implementation co<strong>de</strong> goes here.End SubPublic Sub New(message As String, inner As Exception)' Implementation co<strong>de</strong> goes here.End SubPublic Sub New(info As SerializationInfo, context As StreamingContext)' Implementation co<strong>de</strong> goes here.End SubEnd Class[C#]
public class XxxException : ApplicationException{}public XxxException() {... }public XxxException(string message) {... }public XxxException(string message, Exception inner) {... }public XxxException(SerializationInfo info, StreamingContext context) {...}• En la mayoría <strong>de</strong> los casos, utilice los tipos <strong>de</strong> excepción pre<strong>de</strong>finidos. Defina nuevos tipos <strong>de</strong>excepciones sólo <strong>para</strong> escenarios en los que espera que los usuarios <strong>de</strong> la biblioteca <strong>de</strong> clases<strong>de</strong>tecten las excepciones <strong>de</strong> este nuevo tipo y realicen una acción mediante programación enfunción <strong>de</strong>l tipo <strong>de</strong> excepción. Esto es en vez <strong>de</strong> analizar la ca<strong>de</strong>na <strong>de</strong> la excepción, lo cualtendría un impacto negativo en el rendimiento y en el mantenimiento.Por ejemplo, tiene más sentido <strong>de</strong>finir una excepción FileNotFoundException porque pue<strong>de</strong>ocurrir que el programador <strong>de</strong>cida crear el archivo que falta. No obstante, no es habitualadministrar una excepción FileIOException en el código.• No <strong>de</strong>rive todas las excepciones nuevas directamente <strong>de</strong> la clase base SystemException.Cuando cree nuevas excepciones en espacios <strong>de</strong> nombres System, here<strong>de</strong> sólo <strong>de</strong>SystemException. Cuando cree nuevas excepciones en otros espacios <strong>de</strong> nombres, here<strong>de</strong> <strong>de</strong>ApplicationException.• Agrupe las nuevas excepciones <strong>de</strong>rivadas <strong>de</strong> la clase base SystemException oApplicationException por espacio <strong>de</strong> nombres. Por ejemplo, todas las excepciones System.IOse agrupan bajo IOException (<strong>de</strong>rivada <strong>de</strong> SystemException) y todas las excepcionesMicrosoft.Media se podrían agrupar bajo MediaException (<strong>de</strong>rivada <strong>de</strong> ApplicationException).• Utilice una ca<strong>de</strong>na <strong>de</strong> <strong>de</strong>scripción localizada en cada excepción. Cuando el usuario vea unmensaje <strong>de</strong> error, se <strong>de</strong>rivará <strong>de</strong> la ca<strong>de</strong>na <strong>de</strong> <strong>de</strong>scripción <strong>de</strong> la excepción iniciada y nunca <strong>de</strong>la clase exception.• Genere mensajes <strong>de</strong> error gramaticalmente correctos y con puntuación. Cada frase <strong>de</strong> laca<strong>de</strong>na <strong>de</strong> <strong>de</strong>scripción <strong>de</strong> una excepción <strong>de</strong>be terminar con un punto. El código quenormalmente muestra un mensaje <strong>de</strong> excepción al usuario, no tiene que tratar el caso en el queun programador se olvida <strong>de</strong> incluir un punto final.• Proporcione las propieda<strong>de</strong>s <strong>de</strong> la excepción <strong>para</strong> el acceso mediante programación. Incluyainformación adicional (a<strong>de</strong>más <strong>de</strong> la ca<strong>de</strong>na <strong>de</strong> <strong>de</strong>scripción) en una excepción sólo cuandohaya un escenario mediante programación en el que la información adicional es útil. Seráncontadas las ocasiones en las que necesitará incluir información adicional en una excepción.• No exponga información protegida en mensajes <strong>de</strong> excepciones. Datos como las rutas <strong>de</strong>acceso <strong>de</strong>l sistema <strong>de</strong> archivos local se consi<strong>de</strong>ran información privilegiada. Código maliciosopodría usar esta información <strong>para</strong> recopilar información <strong>de</strong> usuario privada <strong>de</strong>l equipo.• No utilice excepciones en errores normales o esperados, o en el flujo normal <strong>de</strong> control.• Debe <strong>de</strong>volver el valor null en los casos <strong>de</strong> error sumamente habituales. Por ejemplo, uncomando File.Open <strong>de</strong>vuelve una referencia null si no encuentra el archivo, pero inicia unaexcepción si el archivo está bloqueado.
• Diseñe las clases <strong>de</strong> forma que durante su uso normal nunca se inicie una excepción. En elsiguiente ejemplo <strong>de</strong> código, una clase FileStream expone otra forma <strong>de</strong> <strong>de</strong>terminar si se hallegado al final <strong>de</strong>l archivo <strong>para</strong> evitar que se inicie una excepción si el programador lee más allá<strong>de</strong>l final <strong>de</strong>l archivo.[Visual Basic]Class FileReadSub Open()Dim stream As FileStream = File.Open("myfile.txt", FileMo<strong>de</strong>.Open)Dim b As Byte' ReadByte returns -1 at end of file.While b = stream.ReadByte() true' Do something.End WhileEnd SubEnd Class[C#]class FileRead{ void Open()}{ FileStream stream = File.Open("myfile.txt", FileMo<strong>de</strong>.Open);}byte b;// ReadByte returns -1 at end of file.while ((b = stream.ReadByte()) != true){ // Do something. }• Inicie la excepción InvalidOperationException si la llamada a un método o a un <strong>de</strong>scriptor <strong>de</strong>acceso set no es apropiada dado el estado actual <strong>de</strong>l objeto.• Inicie una excepción ArgumentException o genere una excepción <strong>de</strong>rivada <strong>de</strong> esta clase si sepasan o se <strong>de</strong>tectan parámetros no válidos.• Tenga en cuenta que el seguimiento <strong>de</strong> la pila comienza en el punto <strong>de</strong> inicio <strong>de</strong> una excepción,y no en el punto en el que se haya creado con el operador new. Consi<strong>de</strong>re esto cuando <strong>de</strong>cidadon<strong>de</strong> iniciar una excepción.• Utilice los métodos <strong>de</strong> generador <strong>de</strong> excepciones. Es normal que una clase inicie la mismaexcepción <strong>de</strong>s<strong>de</strong> distintos lugares <strong>de</strong> su implementación. Para evitar el código repetitivo, utilicelos métodos auxiliares que crean la excepción y la <strong>de</strong>vuelven utilizando el operador new. En elsiguiente ejemplo <strong>de</strong> código se muestra cómo implementar un método auxiliar.[C#]class File
{ string fileName;public byte[] Read(int bytes){if (!ReadFile(handle, bytes))throw NewFileIOException();}}FileException NewFileIOException(){ string <strong>de</strong>scription =// Build localized string, inclu<strong>de</strong> fileName.return new FileException(<strong>de</strong>scription);}• Inicie excepciones en lugar <strong>de</strong> <strong>de</strong>volver un código <strong>de</strong> error o HRESULT.• Inicie la excepción más específica posible.• Genere un texto <strong>de</strong> mensaje <strong>de</strong>scriptivo <strong>de</strong> las excepciones <strong>para</strong> el programador.• Establezca todos los campos en la excepción que utilice .• Utilice excepciones internas (excepciones enca<strong>de</strong>nadas). Sin embargo, no <strong>de</strong>tecte ni reinicie lasexcepciones a menos que agregue más información o cambie el tipo <strong>de</strong> excepción.• No cree métodos que inicien NullReferenceException o In<strong>de</strong>xOutOfRangeException.• Realice la comprobación <strong>de</strong> argumentos en miembros protegidos (familia) y en miembrosinternos (ensamblado). Indique claramente en la documentación si el método protegido norealiza una comprobación <strong>de</strong> argumentos. A menos que se indique lo contrario, suponga que serealiza la comprobación <strong>de</strong> argumentos. No obstante, si no se realiza esta comprobación pue<strong>de</strong>que el rendimiento mejore.• Limpie cualquier efecto secundario producido al iniciar una excepción. Los llamadores <strong>de</strong>bensuponer que no hay efectos secundarios cuando una función inicia una excepción. Por ejemplo,si un método Hashtable.Insert inicia una excepción, el llamador pue<strong>de</strong> asumir que el elementoespecificado no se ha agregado al método Hashtable.Tipos <strong>de</strong> excepciones estándarEn la tabla siguiente se incluyen las excepciones estándar que proporciona el tiempo <strong>de</strong> ejecución ylas condiciones <strong>para</strong> las que <strong>de</strong>berá crear una clase <strong>de</strong>rivada.Tipo <strong>de</strong> excepción Tipo base Descripción EjemploException Object Clase base <strong>de</strong>todas lasexcepciones.Ninguno (utiliceuna clase<strong>de</strong>rivada <strong>de</strong>esta excepción).
SystemException Exception Clase base <strong>de</strong>todos loserrores quegenera el motor<strong>de</strong> tiempo <strong>de</strong>ejecución.In<strong>de</strong>xOutOfRangeException SystemException La inicia elmotor <strong>de</strong>tiempo <strong>de</strong>ejecución sólocuando no seindizacorrectamenteuna matriz.NullReferenceException SystemException La inicia elmotor <strong>de</strong>tiempo <strong>de</strong>ejecución sólocuando se hacereferencia a unobjeto nulo.InvalidOperationException SystemException La inician losmétodos que seencuentran enun estado noválido.ArgumentException SystemException Clase base <strong>de</strong>todas lasexcepciones <strong>de</strong>argumento.ArgumentNullException ArgumentException La inician losmétodos queno permitenque unargumento seanulo.ArgumentOutOfRangeException ArgumentException La inicianmétodos quecompruebanque losargumentosestán en unintervalo dado.Ninguno (utiliceuna clase<strong>de</strong>rivada <strong>de</strong>esta excepción).Indizar unamatriz fuera <strong>de</strong>lintervalo válido:arr[arr.Length+1]object o = null;o.ToString();Llamar aEnumerator.GetNext() <strong>de</strong>spués<strong>de</strong> eliminar unItem <strong>de</strong> lacolecciónsubyacente.Ninguno (utiliceuna clase<strong>de</strong>rivada <strong>de</strong>esta excepción).String s = null;"Calculate".In<strong>de</strong>xOf (s);String s ="string";s.Chars[9];
ExternalException SystemException Clase base<strong>para</strong>excepcionesque se generano que tienencomo <strong>de</strong>stinoentornos fuera<strong>de</strong>l motor <strong>de</strong>ejecución.COMException ExternalException Excepción queencapsula lainformaciónHresult <strong>de</strong>COM.SEHException ExternalException Excepción queencapsula lainformación <strong>de</strong>controlestructurado <strong>de</strong>excepcionesWin32.Ninguno (utiliceuna clase<strong>de</strong>rivada <strong>de</strong>esta excepción).Se usa en lainteroperabilidad COM.Se utiliza en lainteroperabilidad <strong>de</strong> código noadministrado.Excepciones <strong>de</strong> contenedorLos errores que se generan en el mismo nivel que un componente <strong>de</strong>ben iniciar una excepción<strong>de</strong>scriptiva <strong>para</strong> los usuarios <strong>de</strong> <strong>de</strong>stino. En el siguiente ejemplo <strong>de</strong> código, el mensaje <strong>de</strong> error está<strong>de</strong>stinado a los usuarios <strong>de</strong> la clase TextRea<strong>de</strong>r que intenten leer los datos <strong>de</strong> una secuencia.[Visual Basic]Public Class TextRea<strong>de</strong>rPublic Function ReadLine() As StringTry' Read a line from the stream.Catch e As ExceptionThrow New IOException("Could not read from stream", e)End TryEnd FunctionEnd Class[C#]public class TextRea<strong>de</strong>r{ public string ReadLine(){ try{ // Read a line from the stream. }
}}catch (Exception e){ throw new IOException ("Could not read from stream", e); }
<strong>Instrucciones</strong> <strong>de</strong> uso <strong>de</strong> matricesPara obtener una <strong>de</strong>scripción general <strong>de</strong> matrices y <strong>de</strong>l uso <strong>de</strong> matrices, vea Matrices ySystem.Array (Clase).Matrices y ColeccionesEn algunas ocasiones, los diseñadores <strong>de</strong> bibliotecas <strong>de</strong> clases tienen que <strong>de</strong>cidir entre utilizar unamatriz o <strong>de</strong>volver una colección. Aunque estos tipos tienen mo<strong>de</strong>los <strong>de</strong> uso similares, lascaracterísticas <strong>de</strong> rendimiento son diferentes. En general, se <strong>de</strong>bería usar una colección cuando sepue<strong>de</strong>n utilizar métodos como Add, Remove u otros <strong>para</strong> manipularla.Para obtener más información sobre la utilización <strong>de</strong> colecciones, vea Agrupar datos encolecciones.Usar matricesNo se <strong>de</strong>be <strong>de</strong>volver una instancia interna <strong>de</strong> una matriz, ya que esto permite al código llamadorcambiar la matriz. El ejemplo siguiente muestra cómo la matriz badChars pue<strong>de</strong> ser modificada porcualquier código que tenga acceso a la propiedad Path aunque la propiedad no implemente el<strong>de</strong>scriptor <strong>de</strong> acceso set.[Visual Basic]Imports SystemImports System.CollectionsImports Microsoft.VisualBasicPublic Class ExampleClassNotInheritable Public Class PathPrivate Sub New()End SubPrivate Property PathGetEnd GetSetEnd SetEnd PropertyPrivate Shared badChars() As Char = {Chr(34),""c}Public Shared Function GetInvalidPathChars() As Char()Return badCharsEnd FunctionEnd ClassPublic Shared Sub Main()
' The following co<strong>de</strong> displays the elements of the' array as expected.Dim c As CharFor Each c In Path.GetInvalidPathChars()Console.Write(c)Next cConsole.WriteLine()' The following co<strong>de</strong> sets all the values to A.Path.GetInvalidPathChars()(0) = "A"cPath.GetInvalidPathChars()(1) = "A"cPath.GetInvalidPathChars()(2) = "A"c' The following co<strong>de</strong> displays the elements of the array to the' console. Note that the values have changed.For Each c In Path.GetInvalidPathChars()Console.Write(c)Next cEnd SubEnd Class[C#]using System;using System.Collections;public class ExampleClass{ public sealed class Path{ private Path(){}private static char[] badChars = {'\"', ''};public static char[] GetInvalidPathChars(){ return badChars; }}public static void Main(){ // The following co<strong>de</strong> displays the elements of the// array as expected.foreach(char c in Path.GetInvalidPathChars()){ Console.Write(c); }Console.WriteLine();// The following co<strong>de</strong> sets all the values to A.
}}Path.GetInvalidPathChars()[0] = 'A';Path.GetInvalidPathChars()[1] = 'A';Path.GetInvalidPathChars()[2] = 'A';// The following co<strong>de</strong> displays the elements of the array to the// console. Note that the values have changed.foreach(char c in Path.GetInvalidPathChars()){ Console.Write(c); }El problema <strong>de</strong>l ejemplo anterior se pue<strong>de</strong> corregir aplicando a la colección badChars readonly(ReadOnly en Visual Basic). Como alternativa, se pue<strong>de</strong> clonar la colección badChars antes <strong>de</strong><strong>de</strong>volverla. En el ejemplo siguiente se muestra cómo modificar el método GetInvalidPathChars <strong>para</strong><strong>de</strong>volver un clon <strong>de</strong> la colección badChars.[Visual Basic]Public Shared Function GetInvalidPathChars() As Char()Return CType(badChars.Clone(), Char())End Function[C#]public static char[] GetInvalidPathChars(){ return (char[])badChars.Clone();}No se <strong>de</strong>ben usar campos readonly (ReadOnly en Visual Basic) <strong>de</strong> matrices. Si se hace, la matrizserá <strong>de</strong> sólo lectura y no podrá cambiarse, pero sí se podrán modificar los elementos que lacomponen. En el siguiente ejemplo se muestra cómo se pue<strong>de</strong>n cambiar los elementos <strong>de</strong> la matriz<strong>de</strong> sólo lectura InvalidPathChars.[C#]public sealed class Path{ private Path(){}}public static readonly char[] InvalidPathChars = {'\"', '','|'}'//The following co<strong>de</strong> can be used to change the values in the array.Path.InvalidPathChars[0] = 'A';Usar propieda<strong>de</strong>s indizadas en coleccionesSólo <strong>de</strong>be utilizar una propiedad indizada como miembro pre<strong>de</strong>terminado <strong>de</strong> una interfaz o unaclase <strong>de</strong> la colección. No cree familias <strong>de</strong> funciones en tipos que no sean una colección. Un mo<strong>de</strong>lo<strong>de</strong> métodos, como Add, Item y Count, indica que el tipo <strong>de</strong>be ser una colección.
Propieda<strong>de</strong>s con valores <strong>de</strong> matricesUtilice las colecciones <strong>para</strong> evitar que el código funcione mal. En el siguiente ejemplo <strong>de</strong> código,cada llamada a la propiedad myObj crea una copia <strong>de</strong> la matriz. Como resultado, se crearán 2 n +1copias <strong>de</strong> la matriz en el siguiente bucle.[Visual Basic]Dim i As IntegerFor i = 0 To obj.myObj.Count - 1DoSomething(obj.myObj(i))Next i[C#]for (int i = 0; i < obj.myObj.Count; i++)DoSomething(obj.myObj[i]);Para obtener más información, vea el tema Propieda<strong>de</strong>s y métodos.Devolver matrices vacíasLas propieda<strong>de</strong>s String y Array no <strong>de</strong>ben <strong>de</strong>volver nunca una referencia nula. Este valor pue<strong>de</strong>resultar difícil <strong>de</strong> compren<strong>de</strong>r en este contexto. Por ejemplo, un usuario pue<strong>de</strong> suponer que elsiguiente código funcionará.[Visual Basic]Public Sub DoSomething()Dim s As String = SomeOtherFunc()If s.Length > 0 Then' Do something else.End IfEnd Sub[C#]public void DoSomething(){}string s = SomeOtherFunc();if (s.Length > 0){ // Do something else. }La regla general es que las matrices nulas, las matrices con ca<strong>de</strong>nas vacías ("") y las matricesvacías (0 elementos) <strong>de</strong>ben ser tratadas <strong>de</strong>l mismo modo. Devuelva una matriz vacía en vez <strong>de</strong> unareferencia nula.
<strong>Instrucciones</strong> <strong>de</strong> uso <strong>para</strong> sobrecargar operadoresEn las reglas siguientes se <strong>de</strong>scriben las pautas <strong>para</strong> sobrecargar operadores:• Defina los operadores en tipos <strong>de</strong> valor que sean tipos <strong>de</strong> lenguajes lógicos integrados, como laEstructura System.Decimal.• Proporcione métodos <strong>de</strong> sobrecarga <strong>de</strong> operadores sólo en la clase en la que se <strong>de</strong>finen losmétodos. El compilador <strong>de</strong> C# cumple esta directriz.• Utilice las convenciones <strong>de</strong> firma y <strong>de</strong> nomenclatura <strong>de</strong>scritas en Common LanguageSpecification (CLS). El compilador <strong>de</strong> C# lo hace automáticamente.• Utilice la sobrecarga <strong>de</strong> operadores en los casos en los que el resultado <strong>de</strong> la operación esobvio. Por ejemplo, tiene sentido po<strong>de</strong>r restar un valor Time <strong>de</strong> otro valor Time y obtener unTimeSpan. No obstante, no es a<strong>de</strong>cuado utilizar el operador or <strong>para</strong> crear la unión <strong>de</strong> dosconsultas a la base <strong>de</strong> datos, o utilizar shift <strong>para</strong> escribir una secuencia.• Sobrecargue los operadores <strong>de</strong> forma simétrica. Por ejemplo, si sobrecarga el operador <strong>de</strong>igualdad (==), también <strong>de</strong>be sobrecargar el operador distinto <strong>de</strong> (!=).• Proporcione firmas alternativas. La mayoría <strong>de</strong> los lenguajes no son compatibles con lasobrecarga <strong>de</strong> operadores. Por esta razón, es un requisito <strong>de</strong> CLS que todos los tipos quesobrecargan operadores incluyan un método secundario con un nombre apropiado específico aldominio que proporcione la funcionalidad equivalente. La inclusión <strong>de</strong> un método secundario esun requisito <strong>de</strong> Common Language Specification (CLS). El ejemplo siguiente es compatible conCLS.[C#]public struct DateTime{}public static TimeSpan operator -(DateTime t1, DateTime t2) { }public static TimeSpan Subtract(DateTime t1, DateTime t2) { }La tabla siguiente contiene una lista <strong>de</strong> símbolos <strong>de</strong> operadores y los métodos alternativos ynombres <strong>de</strong> operadores correspondientes.
Símbolo <strong>de</strong> Nombre <strong>de</strong> método alternativo Nombre <strong>de</strong> operadoroperador C++No está <strong>de</strong>finido ToXxx o FromXxx op_ImplicitNo está <strong>de</strong>finido ToXxx o FromXxx op_Explicit+ (binario) Add op_Addition- (binario) Subtract op_Subtraction* (binario) Multiply op_Multiply/ Divi<strong>de</strong> op_Division% Mod op_Modulus^ Xor op_ExclusiveOr& (binario) BitwiseAnd op_BitwiseAnd| BitwiseOr op_BitwiseOr&& And op_LogicalAnd|| Or op_LogicalOr= Assign op_Assign> RightShift op_RightShiftNo está <strong>de</strong>finido LeftShift op_SignedRightShiftNo está <strong>de</strong>finido RightShift op_UnsignedRightShift== Equals op_Equality> Compare op_GreaterThan< Compare op_LessThan!= Compare op_Inequality>= Compare op_GreaterThanOrEqual
<strong>Instrucciones</strong> <strong>para</strong> implementar Equals y el operador<strong>de</strong> igualdad (==)En las reglas siguientes se <strong>de</strong>scriben las pautas <strong>para</strong> implementar el método Equals y el operador<strong>de</strong> igualdad (==).• Implemente el método GetHashCo<strong>de</strong> siempre que implemente el método Equals. De estemodo, los métodos Equals y GetHashCo<strong>de</strong> se mantienen sincronizados.• Reemplace el método Equals siempre que implemente el operador (==), y haga que los dosrealicen las mismas acciones. Esto permite que el código <strong>de</strong> infraestructuras como Hashtable yArrayList, que utilizan el método Equals, se comporten <strong>de</strong>l mismo modo que el código escritopor el usuario con el operador (==).• Reemplace el método Equals siempre que implemente la Interfaz ICom<strong>para</strong>ble.• Tenga en cuenta que <strong>de</strong>be implementar la sobrecarga <strong>de</strong> operadores <strong>para</strong> los operadores <strong>de</strong>igualdad (==), distinto <strong>de</strong> (!=), menor que () cuando implementeICom<strong>para</strong>ble.• No inicie excepciones <strong>de</strong>s<strong>de</strong> los métodos Equals o GetHashCo<strong>de</strong> ni <strong>de</strong>s<strong>de</strong> el operador <strong>de</strong>igualdad (==).Para obtener más información sobre el método Equals, vea Implementar el método Equals.Implementar el operador <strong>de</strong> igualdad (==) en tipos <strong>de</strong> valorEn la mayoría <strong>de</strong> los lenguajes <strong>de</strong> programación no hay un mo<strong>de</strong>lo <strong>de</strong> implementaciónpre<strong>de</strong>terminada <strong>de</strong>l operador <strong>de</strong> igualdad (==) <strong>para</strong> tipos <strong>de</strong> valor. Por consiguiente, <strong>de</strong>berásobrecargar el operador (==) cuando sea necesario.Consi<strong>de</strong>re la posibilidad <strong>de</strong> implementar el método Equals en tipos <strong>de</strong> valor pues la implementaciónpre<strong>de</strong>terminada en System.ValueType no se realiza tan bien como la implementaciónpersonalizada.Implemente el operador (==) siempre que reemplace el método Equals.Implementar el operador <strong>de</strong> igualdad (==) en tipos <strong>de</strong> referenciaEn la mayoría <strong>de</strong> los lenguajes <strong>de</strong> programación no hay un mo<strong>de</strong>lo <strong>de</strong> implementaciónpre<strong>de</strong>terminada <strong>de</strong>l operador <strong>de</strong> igualdad (==) <strong>para</strong> tipos <strong>de</strong> referencia. Por consiguiente, <strong>de</strong>berátener cuidado al implementar el operador (==) en tipos <strong>de</strong> referencia. La mayoría <strong>de</strong> los tipos <strong>de</strong>referencia, incluso los que implementan el método Equals, no <strong>de</strong>ben reemplazar el operador (==).Reemplace el operador (==) si el tipo es un tipo base como Point, String, BigNumber, etc. Siempreque consi<strong>de</strong>re la posibilidad <strong>de</strong> sobrecargar los operadores <strong>de</strong> adición (+) y <strong>de</strong> sustracción (-),<strong>de</strong>berá consi<strong>de</strong>rar también la sobrecarga <strong>de</strong>l operador (==).
<strong>Instrucciones</strong> <strong>de</strong> tipos <strong>de</strong> conversiónEn las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> uso <strong>de</strong> conversiones:• No permita conversiones implícitas que <strong>de</strong>n como resultado una pérdida <strong>de</strong> precisión. Porejemplo, no <strong>de</strong>be haber una conversión implícita <strong>de</strong> Double a Int32, pero pue<strong>de</strong> haber una <strong>de</strong>Int32 a Int64.• No inicie excepciones en conversiones implícitas porque es muy difícil que el programadorcomprenda lo que está pasando.• Proporcione conversiones que funcionen en todo el objeto. El valor que se convierte <strong>de</strong>berepresentar todo el objeto, no un miembro <strong>de</strong> un objeto. Por ejemplo, no es correcto convertir unButton en una ca<strong>de</strong>na mediante la <strong>de</strong>volución <strong>de</strong> su título.• No genere un valor que sea semánticamente distinto. Por ejemplo, es conveniente convertir unvalor DateTime o TimeSpan a un valor Int32. El valor Int32 sigue representando el tiempo o laduración. No tiene sentido, sin embargo convertir una ca<strong>de</strong>na <strong>de</strong> nombre <strong>de</strong> archivo como"c:\mybitmap.gif" a un objeto Bitmap.• No convierta valores <strong>de</strong> dominios diferentes. Las conversiones funcionan <strong>de</strong>ntro <strong>de</strong> un dominioespecífico <strong>de</strong> valores. Por ejemplo, los números y las ca<strong>de</strong>nas pertenecen a distintos dominios.Tiene sentido convertir un valor Int32 a Double. Sin embargo, no tiene sentido convertir unvalor Int32 a String, ya que estos dos valores se encuentran en dominios diferentes.
Mo<strong>de</strong>los <strong>de</strong> diseño comunesEn este tema se proporcionan las instrucciones <strong>de</strong> implementación <strong>de</strong> mo<strong>de</strong>los <strong>de</strong> diseño comunesen las bibliotecas <strong>de</strong> clases.Implementar Finalize y Dispose <strong>para</strong> limpiar recursosno administradosA menudo, las instancias <strong>de</strong> clases encapsulan el control sobre los recursos que no administra elmotor <strong>de</strong> tiempo <strong>de</strong> ejecución, como i<strong>de</strong>ntificadores <strong>de</strong> ventanas (HWND), conexiones a bases <strong>de</strong>datos, etc. Por consiguiente, <strong>de</strong>berá proporcionar las formas explícita e implícita <strong>de</strong> liberar esosrecursos. Proporcione el control implícito implementando el Método Finalize protegido en un objeto(sintaxis <strong>de</strong> <strong>de</strong>structor <strong>de</strong> C# y <strong>de</strong> Extensiones administradas <strong>de</strong> C++). El recolector <strong>de</strong> elementosno utilizados llama a estos métodos en algún momento, cuando ya no hay ninguna referencia válidaal objeto.En algunos casos, se pue<strong>de</strong> proporcionar a los <strong>programadores</strong> la utilización <strong>de</strong> un objeto concapacidad <strong>para</strong> liberar explícitamente estos recursos externos, antes <strong>de</strong> que el recolector <strong>de</strong>elementos no utilizados libere el objeto. Si el recurso externo es insuficiente o resulta muy caro, sepue<strong>de</strong> conseguir un mejor rendimiento si el programador libera explícitamente los recursos cuandoya no se utilizan. Para proporcionar el control explícito, se implementa el método Dispose queproporciona la interfaz IDisposable. El consumidor <strong>de</strong>l objeto <strong>de</strong>berá llamar a este método una vezhecho esto utilizando el objeto. Se pue<strong>de</strong> llamar al método Dispose aunque todavía existanreferencias al objeto.Hay que tener en cuenta que cuando se proporciona el control explícito mediante el métodoDispose, se <strong>de</strong>be proporcionar la limpieza implícita utilizando el método Finalize. Finalizeproporciona una copia <strong>de</strong> seguridad <strong>para</strong> evitar que los recursos se pierdan permanentemente si elprogramador no llama al método Dispose.Para obtener más información sobre la implementación <strong>de</strong> los métodos Finalize y Dispose <strong>para</strong>limpiar los recursos no administrados, vea Programar <strong>para</strong> la recolección <strong>de</strong> elementos noutilizados. En el siguiente ejemplo <strong>de</strong> código se muestra el mo<strong>de</strong>lo <strong>de</strong> diseño básico <strong>de</strong>implementación <strong>de</strong>l método Dispose.[Visual Basic]' Design pattern for a base class.Public Class BaseImplements IDisposable' Implement IDisposable.Public Overloads Sub Dispose() Implements IDisposable.DisposeDispose(True)GC.SuppressFinalize(Me)End SubProtected Overloads Overridable Sub Dispose(disposing As Boolean)
If disposing Then' Free other state (managed objects).End If' Free your own state (unmanaged objects).' Set large fields to null.End SubProtected Overri<strong>de</strong>s Sub Finalize()' Simply call Dispose(False).Dispose (False)End SubEnd Class' Design pattern for a <strong>de</strong>rived class.Public Class DerivedInherits BaseProtected Overloads Overri<strong>de</strong>s Sub Dispose(disposing As Boolean)If disposing Then' Release managed resources.End If' Release unmanaged resources.' Set large fields to null.' Call Dispose on your base class.Mybase.Dispose(disposing)End Sub' The <strong>de</strong>rived class does not have a Finalize method' or a Dispose method with <strong>para</strong>meters because it inherits' them from the base class.End Class[C#]// Design pattern for a base class.public class Base: IDisposable{//Implement IDisposable.public void Dispose(){Dispose(true);GC.SuppressFinalize(this);
}protected virtual void Dispose(bool disposing){if (disposing){ // Free other state (managed objects). }// Free your own state (unmanaged objects).// Set large fields to null.}// Use C# <strong>de</strong>structor syntax for finalization co<strong>de</strong>.~Base(){ // Simply call Dispose(false).Dispose (false);}// Design pattern for a <strong>de</strong>rived class.public class Derived: Base{}protected overri<strong>de</strong> void Dispose(bool disposing){}if (disposing){ // Release managed resources. }// Release unmanaged resources.// Set large fields to null.// Call Dispose on your base class.base.Dispose(disposing);// The <strong>de</strong>rived class does not have a Finalize method// or a Dispose method with <strong>para</strong>meters because it inherits// them from the base class.Para obtener un ejemplo <strong>de</strong> código más <strong>de</strong>tallado <strong>de</strong>l mo<strong>de</strong>lo <strong>de</strong> diseño <strong>de</strong> implementación <strong>de</strong> losmétodos Finalize y Dispose, vea Implementar un método Dispose.Personalizar el nombre <strong>de</strong>l método DisposeEn algunos casos, es más a<strong>de</strong>cuado utilizar un nombre específico <strong>de</strong>l dominio que utilizar Dispose.Por ejemplo, en la encapsulación <strong>de</strong> un archivo se pue<strong>de</strong> utilizar Close como el nombre <strong>de</strong>l método.
En este caso, se implementa Dispose <strong>de</strong> forma privada y se crea un método Close público quellama a Dispose. En el siguiente ejemplo <strong>de</strong> código se muestra este mo<strong>de</strong>lo. Se pue<strong>de</strong> reemplazarClose con un nombre <strong>de</strong> método a<strong>de</strong>cuado al dominio.[Visual Basic]' Do not make this method overridable.' A <strong>de</strong>rived class should not be allowed' to overri<strong>de</strong> this method.Public Sub Close()' Call the Dispose method with no <strong>para</strong>meters.Dispose()End Sub[C#]// Do not make this method virtual.// A <strong>de</strong>rived class should not be allowed// to overri<strong>de</strong> this method.public void Close(){ // Call the Dispose method with no <strong>para</strong>meters.}FinalizeDispose();En las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> uso <strong>de</strong>l método Finalize:• Implemente Finalize sólo en objetos que requieran finalización. Hay algunos costos <strong>de</strong>rendimiento asociados con los métodos Finalize.• Si requiere un método Finalize, consi<strong>de</strong>re la posibilidad <strong>de</strong> implementar IDisposable <strong>para</strong> quelos usuarios <strong>de</strong> la clase puedan evitar el costo <strong>de</strong> invocar el método Finalize.• No haga más visible el método Finalize. Este método <strong>de</strong>be ser <strong>de</strong> tipo protected y no <strong>de</strong> tipopublic.• El método Finalize <strong>de</strong> un objeto <strong>de</strong>be liberar todos los recursos externos que posea el objeto.A<strong>de</strong>más, un método Finalize <strong>de</strong>be liberar sólo los recursos que contiene el objeto. El métodoFinalize no <strong>de</strong>be hacer referencia a ningún otro objeto.• No llame directamente al método Finalize <strong>de</strong> un objeto que no sea <strong>de</strong> la clase base <strong>de</strong>l objeto.Ésta no es una operación válida en el lenguaje <strong>de</strong> programación C#.• Llame al método base.Finalize <strong>de</strong>s<strong>de</strong> un método Finalize <strong>de</strong>l objeto.DesecharNota Al método Finalize <strong>de</strong> la clase base se llama automáticamente con la sintaxis <strong>de</strong><strong>de</strong>structor <strong>de</strong> C# y <strong>de</strong> las Extensiones administradas <strong>de</strong> C++.En las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> uso <strong>de</strong>l método Dispose:
• Implemente el mo<strong>de</strong>lo <strong>de</strong> diseño Dispose en un tipo que encapsula recursos que <strong>de</strong>ben serliberados explícitamente. Los usuarios pue<strong>de</strong>n liberar recursos externos llamando al métodoDispose público.• Implemente el mo<strong>de</strong>lo <strong>de</strong> diseño Dispose en un tipo base que habitualmente tiene tipos<strong>de</strong>rivados que contienen recursos, aunque la clase base no contenga ninguno. Si el tipo basecontiene un método Close, esto suele indicar que se <strong>de</strong>be implementar el método Dispose. Eneste caso, no implemente un método Finalize en el tipo base. Finalize se <strong>de</strong>be implementar entodos los tipos <strong>de</strong>rivados con recursos que es necesario limpiar.• Libere todos los recursos <strong>de</strong>sechables que contenga un tipo en su método Dispose.• Una vez llamado el método Dispose en una instancia, evite ejecutar el método Finalizemediante una llamada al Método GC.SuppressFinalize. La excepción a esta regla son lassituaciones muy poco habituales en las que el trabajo que no realiza el método Dispose se<strong>de</strong>be hacer con el método Finalize.• Llame al método Dispose <strong>de</strong> la clase base si implementa la interfaz IDisposable.• No dé por supuesto que se llamará al método Dispose. Los recursos no administrados quecontiene un tipo también se <strong>de</strong>ben liberar en un método Finalize, en el caso <strong>de</strong> que no se llameal método Dispose.• Inicie una excepción ObjectDisposedException a partir <strong>de</strong> los métodos <strong>de</strong> instancias <strong>de</strong> estetipo (distintos <strong>de</strong> Dispose) una vez que se haya <strong>de</strong>shecho <strong>de</strong> los recursos. Esta regla no seaplica al método Dispose porque se <strong>de</strong>bería po<strong>de</strong>r llamarlo varias veces sin iniciar unaexcepción.• Propague las llamadas al método Dispose a través <strong>de</strong> la jerarquía <strong>de</strong> tipos base. El métodoDispose <strong>de</strong>be liberar todos los recursos que contenga un objeto y todos los objetos quecontenga ese objeto. Por ejemplo, pue<strong>de</strong> crear un objeto como TextRea<strong>de</strong>r que contenga losobjetos Stream y Encoding, los dos creados por TextRea<strong>de</strong>r sin el conocimiento <strong>de</strong>l usuario.A<strong>de</strong>más, Stream y Encoding pue<strong>de</strong>n adquirir recursos externos. Cuando llame al métodoDispose en TextRea<strong>de</strong>r, éste <strong>de</strong>berá llamar a su vez al método Dispose en los objetos Streamy Encoding, que liberarán los recursos externos.• Consi<strong>de</strong>re la posibilidad <strong>de</strong> prohibir la utilización <strong>de</strong> un objeto <strong>de</strong>spués <strong>de</strong> llamar al métodoDispose. La acción <strong>de</strong> volver a crear un objeto una vez <strong>de</strong>sechado es un mo<strong>de</strong>lo difícil <strong>de</strong>implementar.• Permita que se llame al método Dispose más <strong>de</strong> una vez sin iniciar excepciones. El método no<strong>de</strong>be realizar ninguna acción <strong>de</strong>spués <strong>de</strong> la primera llamada.
Implementar el método EqualsPara obtener más información sobre la implementación <strong>de</strong>l operador <strong>de</strong> igualdad (==), vea<strong>Instrucciones</strong> <strong>para</strong> implementar Equals y el operador <strong>de</strong> igualdad (==).• Reemplace el método GetHashCo<strong>de</strong> <strong>para</strong> permitir que un tipo que funcione correctamente enuna tabla hash.• No inicie una excepción en la implementación <strong>de</strong> un método Equals. En vez <strong>de</strong> esto, <strong>de</strong>vuelvaun valor false <strong>para</strong> un argumento nulo.• Siga el contrato <strong>de</strong>finido en el Método Object.Equals <strong>de</strong> la forma siguiente:• x.Equals(x) <strong>de</strong>vuelve true.• x.Equals(y) <strong>de</strong>vuelve el mismo valor que y.Equals(x).• (x.Equals(y) && y.Equals(z)) <strong>de</strong>vuelve el valor true si y solo si x.Equals(z) <strong>de</strong>vuelve el valortrue.• Las invocaciones sucesivas <strong>de</strong> x.Equals(y) <strong>de</strong>vuelven el mismo valor siempre que no semodifiquen los objetos a los que se hace referencia mediante x e y.• x.Equals(null) <strong>de</strong>vuelve el valor false.• En algunas clases <strong>de</strong> objetos, es conveniente realizar una comprobación <strong>de</strong> Equals <strong>para</strong>comprobar así la igualdad <strong>de</strong> valores en lugar <strong>de</strong> la igualdad referencial. Este tipo <strong>de</strong>implementaciones <strong>de</strong> Equals <strong>de</strong>vuelve el valor true si los dos objetos tienen el mismo valor,aunque no sean la misma instancia. La <strong>de</strong>finición <strong>de</strong> lo que constituye el valor <strong>de</strong> un objeto estáen función <strong>de</strong>l implementador <strong>de</strong>l tipo, pero suele ser una parte o la totalidad <strong>de</strong> los datosalmacenados en las variables <strong>de</strong> la instancia <strong>de</strong>l objeto. Por ejemplo, el valor <strong>de</strong> una ca<strong>de</strong>na sebasa en los caracteres <strong>de</strong> la ca<strong>de</strong>na; el método Equals <strong>de</strong> la clase String <strong>de</strong>vuelve un valortrue en el caso <strong>de</strong> que dos ca<strong>de</strong>nas <strong>de</strong> la ca<strong>de</strong>na contengan exactamente los mismoscaracteres y en el mismo or<strong>de</strong>n.• Cuando el método Equals <strong>de</strong> una clase base proporciona igualdad <strong>de</strong> valores, un reemplazo<strong>de</strong>l método Equals <strong>de</strong> una clase <strong>de</strong>rivada <strong>de</strong>berá llamar a la implementación heredada <strong>de</strong>Equals.• Si programa en un lenguaje que es compatible con la sobrecarga <strong>de</strong> operadores y eligesobrecargar el operador <strong>de</strong> igualdad (==) <strong>para</strong> un tipo especificado, este tipo <strong>de</strong>berá reemplazarel método Equals. Este tipo <strong>de</strong> implementaciones <strong>de</strong>l método Equals <strong>de</strong>be <strong>de</strong>volver los mismosresultados que el operador <strong>de</strong> igualdad. El seguimiento <strong>de</strong> esta instrucción garantiza que elcódigo <strong>de</strong> la biblioteca <strong>de</strong> clases que utiliza el método Equals (como ArrayList y Hashtable)funciona <strong>de</strong> manera coherente con respecto a la forma en que el código <strong>de</strong> la aplicación utilizael operador <strong>de</strong> igualdad.• Si implementa un tipo <strong>de</strong> valor, consi<strong>de</strong>re la posibilidad <strong>de</strong> reemplazar el método Equals <strong>para</strong>obtener un rendimiento mayor que el que proporciona la implementación pre<strong>de</strong>terminada <strong>de</strong>lmétodo Equals en System.ValueType. Si reemplaza Equals y el lenguaje es compatible con lasobrecarga <strong>de</strong> operadores, <strong>de</strong>be sobrecargar el operador <strong>de</strong> igualdad <strong>de</strong>l tipo <strong>de</strong> valor.• Si implementa tipos <strong>de</strong> referencia, <strong>de</strong>be consi<strong>de</strong>rar la posibilidad <strong>de</strong> reemplazar el métodoEquals en un tipo <strong>de</strong> referencia si el tipo <strong>de</strong> referencia se parece a un tipo base como Point,String, BigNumber, etc. La mayoría <strong>de</strong> los tipos <strong>de</strong> referencia no <strong>de</strong>ben sobrecargar el operador<strong>de</strong> igualdad, aunque reemplacen el método Equals. Sin embargo, si implementa un tipo <strong>de</strong>
eferencia <strong>de</strong>stinado a contener semántica <strong>de</strong> valores, como un tipo <strong>de</strong> número complejo,<strong>de</strong>berá reemplazar el operador <strong>de</strong> igualdad.• Si implementa la Interfaz ICom<strong>para</strong>ble en un tipo dado, <strong>de</strong>berá reemplazar el método Equals enese tipo.EjemplosImplementar el método EqualsEl siguiente ejemplo <strong>de</strong> código contiene dos llamadas a la implementación pre<strong>de</strong>terminada <strong>de</strong>lmétodo Equals.[Visual Basic]Imports SystemClass SampleClassPublic Shared Sub Main()Dim obj1 As New System.Object()Dim obj2 As New System.Object()Console.WriteLine(obj1.Equals(obj2))obj1 = obj2Console.WriteLine(obj1.Equals(obj2))End SubEnd Class[C#]using System;class SampleClass{}public static void Main(){}Object obj1 = new Object();Object obj2 = new Object();Console.WriteLine(obj1.Equals(obj2));obj1 = obj2;Console.WriteLine(obj1.Equals(obj2));El resultado <strong>de</strong>l código anterior es el siguiente.FalseTrueReemplazar el método Equals
En el siguiente ejemplo <strong>de</strong> código se muestra una clase Point que reemplaza el método Equals<strong>para</strong> proporcionar igualdad <strong>de</strong> valores y una clase Point3D <strong>de</strong>rivada <strong>de</strong> Point. Dado que elreemplazo <strong>de</strong> la clase Point por parte <strong>de</strong> Equals es el primero en la ca<strong>de</strong>na <strong>de</strong> herencia <strong>para</strong>introducir la igualdad <strong>de</strong> valores, no se invoca al método Equals <strong>de</strong> la clase base (que se hereda <strong>de</strong>Object y comprueba la igualdad referencial). No obstante, Point3D.Equals invoca a Point.Equalsporque Point implementa Equals <strong>de</strong> forma que se proporciona igualdad <strong>de</strong> valores.[Visual Basic]Imports SystemClass PointPrivate x As IntegerPrivate y As IntegerPublic Overri<strong>de</strong>s Overloads Function Equals(obj As Object) As Boolean' Check for null values and compare run-time types.If obj Is Nothing Or Not Me.GetType() Is obj.GetType() ThenReturn FalseEnd IfDim p As Point = CType(obj, Point)Return Me.x = p.x And Me.y = p.yEnd FunctionEnd ClassClass Point3DInherits PointPrivate z As IntegerPublic Overri<strong>de</strong>s Overloads Function Equals(obj As Object) As BooleanReturn MyBase.Equals(obj) And z = CType(obj, Point3D).zEnd FunctionPublic Overri<strong>de</strong>s Function GetHashCo<strong>de</strong>() As IntegerReturn MyBase.GetHashCo<strong>de</strong>() ^ zEnd FunctionEnd Class[C#]using System;
class Point: object{int x, y;public overri<strong>de</strong> bool Equals(Object obj){// Check for null values and compare run-time types.if (obj == null || GetType() != obj.GetType())return false;Point p = (Point)obj;return (x == p.x) && (y == p.y);}public overri<strong>de</strong> int GetHashCo<strong>de</strong>(){return x ^ y;}}class Point3D: Point{}int z;public overri<strong>de</strong> bool Equals(Object obj){}return base.Equals(obj) && z == ((Point3D)obj).z;public overri<strong>de</strong> int GetHashCo<strong>de</strong>(){}return base.GetHashCo<strong>de</strong>() ^ z;El método Point.Equals comprueba que el argumento obj no es null y que hace referencia a unainstancia <strong>de</strong>l mismo tipo que este objeto. Si alguna <strong>de</strong> las comprobaciones no es satisfactoria, elmétodo <strong>de</strong>vuelve el valor false. El método Equals utiliza el Método Object.GetType <strong>para</strong> <strong>de</strong>terminarsi los tipos en tiempo <strong>de</strong> ejecución <strong>de</strong> los dos objetos son idénticos. Conviene señalar que typeof(TypeOf en Visual Basic) no se utiliza aquí porque <strong>de</strong>vuelve el tipo estático. Si en vez <strong>de</strong> esto elmétodo ha utilizado una comprobación <strong>de</strong> la forma obj is Point, la comprobación <strong>de</strong>volverá el valortrue en los casos en los que obj sea una instancia <strong>de</strong> una clase <strong>de</strong>rivada <strong>de</strong> Point, aunque obj y lainstancia actual no tengan el mismo tipo en tiempo <strong>de</strong> ejecución. Una vez comprobado que ambos
objetos son <strong>de</strong>l mismo tipo, el método convierte obj en el tipo Point y <strong>de</strong>vuelve el resultado <strong>de</strong> lacom<strong>para</strong>ción <strong>de</strong> las variables <strong>de</strong> instancia <strong>de</strong> los dos objetos.En Point3D.Equals, se invoca al método Equals heredado antes <strong>de</strong> realizar otras acciones. Elmétodo Equals heredado comprueba si obj no es null, si obj es una instancia <strong>de</strong> la misma clase queel objeto y si las variables <strong>de</strong> la instancia heredada coinci<strong>de</strong>n. Sólo cuando el método Equalsheredado <strong>de</strong>vuelva el valor true, com<strong>para</strong>rá el método las variables <strong>de</strong> instancia introducidas en laclase <strong>de</strong>rivada. Concretamente, la conversión a Point3D no se ejecuta salvo que se haya<strong>de</strong>terminado que obj es <strong>de</strong> tipo Point3D o una clase <strong>de</strong>rivada <strong>de</strong> Point3D.Utilizar el método Equals <strong>para</strong> com<strong>para</strong>r variables <strong>de</strong> instanciaEn el ejemplo anterior, el operador <strong>de</strong> igualdad (==) se utiliza <strong>para</strong> com<strong>para</strong>r las variables <strong>de</strong>instancia individuales. En algunos casos, <strong>de</strong>be utilizarse el método Equals <strong>para</strong> com<strong>para</strong>r variables<strong>de</strong> instancia en una implementación <strong>de</strong> Equals, como se muestra en el siguiente ejemplo <strong>de</strong> código.[Visual Basic]Imports SystemClass RectanglePrivate a, b As PointPublic Overri<strong>de</strong>s Overloads Function Equals(obj As [Object]) As BooleanIf obj Is Nothing Or Not Me.GetType() Is obj.GetType() ThenReturn FalseEnd IfDim r As Rectangle = CType(obj, Rectangle)' Use Equals to compare instance variables.Return Me.a.Equals(r.a) And Me.b.Equals(r.b)End FunctionPublic Overri<strong>de</strong>s Function GetHashCo<strong>de</strong>() As IntegerReturn a.GetHashCo<strong>de</strong>() ^ b.GetHashCo<strong>de</strong>()End FunctionEnd Class[C#]using System;class Rectangle{Point a, b;public overri<strong>de</strong> bool Equals(Object obj){
}if (obj == null || GetType() != obj.GetType()) return false;Rectangle r = (Rectangle)obj;// Use Equals to compare instance variables.return a.Equals(r.a) && b.Equals(r.b);}public overri<strong>de</strong> int GetHashCo<strong>de</strong>(){return a.GetHashCo<strong>de</strong>() ^ b.GetHashCo<strong>de</strong>();}Sobrecargar el operador <strong>de</strong> igualdad (==) y el método EqualsAlgunos lenguajes <strong>de</strong> programación, como el lenguaje C#, admiten la sobrecarga <strong>de</strong> operadores.Cuando un tipo sobrecarga el operador (==), también <strong>de</strong>be reemplazar el método Equals <strong>para</strong>proporcionar la misma funcionalidad. Esto se consigue normalmente escribiendo el método Equalsen términos <strong>de</strong>l operador <strong>de</strong> igualdad (==) sobrecargado, como en el siguiente ejemplo.[C#]public struct Complex{}double re, im;public overri<strong>de</strong> bool Equals(Object obj){}return obj is Complex && this == (Complex)obj;public overri<strong>de</strong> int GetHashCo<strong>de</strong>(){}return re.GetHashCo<strong>de</strong>() ^ im.GetHashCo<strong>de</strong>();public static bool operator ==(Complex x, Complex y){}return x.re == y.re && x.im == y.im;public static bool operator !=(Complex x, Complex y){}return !(x == y);
Como Complex es un tipo <strong>de</strong> valor struct <strong>de</strong> C#, se sabe que no se <strong>de</strong>rivará ninguna clase <strong>de</strong>Complex. Por tanto, el método Equals no necesita com<strong>para</strong>r los resultados <strong>de</strong> GetType <strong>para</strong> cadaobjeto. En su lugar utiliza el operador is <strong>para</strong> comprobar el parámetro obj.
Uso <strong>de</strong> las funciones <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamadaLos Delegados, las Interfaces y los Eventos permiten proporcionar la funcionalidad <strong>de</strong> <strong>de</strong>volución <strong>de</strong>llamada. Cada tipo tiene sus propias características <strong>de</strong> uso que se adaptan perfectamente asituaciones específicas.EventosUtilice un evento si se cumplen las siguientes condiciones:• Un método solicita por a<strong>de</strong>lantado la función <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada, normalmente, a través<strong>de</strong> métodos Add y Remove distintos.• Normalmente, hay más <strong>de</strong> un objeto que requiere la notificación <strong>de</strong>l evento.• Desea que los usuarios finales puedan agregar fácilmente un agente <strong>de</strong> escucha a lanotificación en el diseñador visual.DelegadosUtilice un <strong>de</strong>legado si se cumplen las siguientes condiciones:• Desea un puntero a función con estilo <strong>de</strong> lenguaje C.• Desea una función <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada sencilla.• Desea que el registro se produzca en la llamada o durante la construcción y no a través <strong>de</strong> unmétodo Add distinto.InterfacesUtilice una interfaz si la función <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada requiere un comportamiento complejo.Uso <strong>de</strong> tiempos <strong>de</strong> esperaUtilice los tiempos <strong>de</strong> espera <strong>para</strong> especificar el tiempo máximo que un llamador está dispuesto aesperar <strong>para</strong> finalizar una llamada al método.Los tiempos <strong>de</strong> espera pue<strong>de</strong>n adoptar el formato <strong>de</strong> un parámetro en la llamada al método, comose muestra a continuación.[Visual Basic]server.PerformOperation(timeout)[C#]server.PerformOperation(timeout);Otra posibilidad es utilizar los tiempos <strong>de</strong> espera como una propiedad <strong>de</strong> la clase <strong>de</strong> servidor, comose muestra a continuación.[Visual Basic]server.Timeout = timeoutserver.PerformOperation()[C#]server.Timeout = timeout;
server.PerformOperation();Conviene utilizar el primer enfoque, porque la asociación entre la operación y el tiempo <strong>de</strong> esperaes más clara. El enfoque basado en la propiedad pue<strong>de</strong> mejorar si la clase <strong>de</strong> servidor se diseña<strong>para</strong> ser un componente que se utilice con diseñadores visuales.Tradicionalmente, los tiempos <strong>de</strong> espera se han representado mediante enteros. Resultacomplicado utilizar números enteros, ya que la unidad <strong>de</strong>l tiempo <strong>de</strong> espera no es evi<strong>de</strong>nte y esdifícil convertir las unida<strong>de</strong>s <strong>de</strong> tiempo a los milisegundos que se utilizan habitualmente.El mejor enfoque es utilizar la estructura TimeSpan como tipo <strong>de</strong> tiempo <strong>de</strong> espera. TimeSpanresuelve los problemas con los números enteros <strong>de</strong> los tiempos <strong>de</strong> espera mencionadosanteriormente. En el siguiente ejemplo <strong>de</strong> código se muestra cómo utilizar un tiempo <strong>de</strong> espera <strong>de</strong>tipo TimeSpan.[Visual Basic]Public Class ServerPublic Sub PerformOperation(timeout As TimeSpan timeout)' Insert co<strong>de</strong> for the method here.End SubEnd ClassPublic Class TestClassDim server As New Server();server.PerformOperation(New TimeSpan(0,15,0))End Class[C#]public class Server{void PerformOperation(TimeSpan timeout){// Insert co<strong>de</strong> for the method here.}}public class TestClass{}public Server server = new Server();server.PerformOperation(new TimeSpan(0,15,0));Si el tiempo <strong>de</strong> espera se establece en TimeSpan(0), el método <strong>de</strong>berá iniciar una excepción si laoperación no se completa inmediatamente. Si el tiempo <strong>de</strong> espera es TimeSpan.MaxValue, la
operación <strong>de</strong>be esperar siempre sin que se agote el tiempo <strong>de</strong> espera, como si no se hubieseestablecido el tiempo <strong>de</strong> espera. No es necesario que la clase <strong>de</strong> servidor sea compatible conninguno <strong>de</strong> estos valores, aunque <strong>de</strong>berá iniciar una InvalidArgumentException si no admite elvalor <strong>de</strong> tiempo espera especificado.Si se agota el tiempo <strong>de</strong> espera y se inicia una excepción, la clase <strong>de</strong> servidor <strong>de</strong>berá cancelar laoperación subyacente.Si se utiliza un tiempo <strong>de</strong> espera pre<strong>de</strong>terminado, la clase <strong>de</strong> servidor <strong>de</strong>berá incluir una propiedad<strong>de</strong>faultTimeout estática que se utilizará si el usuario no la especifica. En el siguiente ejemplo <strong>de</strong>código se incluye una propiedad OperationTimeout estática <strong>de</strong> tipo TimeSpan que <strong>de</strong>vuelve<strong>de</strong>faultTimeout.[Visual Basic]Class ServerPrivate <strong>de</strong>faultTimeout As New TimeSpan(1000)Overloads Sub PerformOperation()Me.PerformOperation(OperationTimeout)End SubOverloads Sub PerformOperation(timeout As TimeSpan)' Insert co<strong>de</strong> here.End SubReadOnly Property OperationTimeout() As TimeSpanGetReturn <strong>de</strong>faultTimeoutEnd GetEnd PropertyEnd Class[C#]class Server{TimeSpan <strong>de</strong>faultTimeout = new TimeSpan(1000);void PerformOperation(){ this.PerformOperation(OperationTimeout); }void PerformOperation(TimeSpan timeout){ // Insert co<strong>de</strong> here. }
TimeSpan OperationTimeout{ get{ return <strong>de</strong>faultTimeout; }}}Los tipos que no pue<strong>de</strong>n resolver los tiempos <strong>de</strong> espera <strong>de</strong> un TimeSpan <strong>de</strong>berán redon<strong>de</strong>ar eltiempo <strong>de</strong> espera al intervalo más cercano. Por ejemplo, un tipo que sólo pue<strong>de</strong> esperar enincrementos <strong>de</strong> un segundo <strong>de</strong>berá redon<strong>de</strong>ar al segundo más cercano. La excepción a esta reglaes cuando se redon<strong>de</strong>a un valor a cero. En este caso, el tiempo <strong>de</strong> espera <strong>de</strong>be redon<strong>de</strong>arse haciaarriba al valor <strong>de</strong> tiempo <strong>de</strong> espera mínimo posible. Al redon<strong>de</strong>ar a un número distinto <strong>de</strong> cero seevitan los bucles "busy-wait", en los que un tiempo <strong>de</strong> espera <strong>de</strong> valor cero provoca un uso <strong>de</strong>lprocesador <strong>de</strong>l 100 por ciento.A<strong>de</strong>más, es conveniente iniciar una excepción cuando se agota el tiempo <strong>de</strong> espera, en vez <strong>de</strong><strong>de</strong>volver un código <strong>de</strong> error. Si se agota el tiempo <strong>de</strong> espera significa que la operación no se hapodido completar correctamente y, por tanto, se <strong>de</strong>be tratar y administrar como cualquier otro erroren tiempo <strong>de</strong> ejecución. Para obtener más información, vea <strong>Instrucciones</strong> <strong>para</strong> provocar y controlarerrores.En el caso <strong>de</strong> una operación asincrónica con un tiempo <strong>de</strong> espera, se <strong>de</strong>be llamar a la función <strong>de</strong><strong>de</strong>volución <strong>de</strong> llamada e iniciar una excepción la primera vez que se tiene acceso a los resultados<strong>de</strong> la operación. Esto se muestra en el siguiente ejemplo <strong>de</strong> código:[Visual Basic]Sub OnReceiveCompleted(ByVal sen<strong>de</strong>r As System.Object, ByVal asyncResult AsReceiveCompletedEventArgs)Dim queue As MessageQueue = CType(sen<strong>de</strong>r, MessageQueue)' The following co<strong>de</strong> will throw an exception' if BeginReceive has timed out.Dim message As Message = queue.EndReceive(asyncResult.AsyncResult)Console.WriteLine(("Message: " + CStr(message.Body)))queue.BeginReceive(New TimeSpan(1, 0, 0))End Sub[C#]void OnReceiveCompleted(Object sen<strong>de</strong>r, ReceiveCompletedEventArgs asyncResult){MessageQueue queue = (MessageQueue) sen<strong>de</strong>r;// The following co<strong>de</strong> will throw an exception// if BeginReceive has timed out.Message message = queue.EndReceive(asyncResult.AsyncResult);Console.WriteLine("Message: " + (string)message.Body);queue.BeginReceive(new TimeSpan(1,0,0));
}Para obtener más información, vea <strong>Instrucciones</strong> <strong>para</strong> programación asincrónica.
Seguridad en las bibliotecas <strong>de</strong> clasesLos diseñadores <strong>de</strong> bibliotecas <strong>de</strong> clases <strong>de</strong>ben compren<strong>de</strong>r la seguridad <strong>de</strong> acceso a código <strong>para</strong>escribir bibliotecas <strong>de</strong> clases seguras. Cuando se escribe una biblioteca <strong>de</strong> clases, hay que tener encuenta dos principios <strong>de</strong> seguridad: proteger los objetos con permisos y escribir código <strong>de</strong> plenaconfianza. El grado en que se apliquen estos dos principios <strong>de</strong>pen<strong>de</strong>rá <strong>de</strong> la clase que se escriba.Algunas clases, como la Clase System.IO.FileStream, representan objetos que es necesarioproteger con permisos. La implementación <strong>de</strong> estas clases es responsable <strong>de</strong> comprobar lospermisos <strong>de</strong> llamadores y <strong>de</strong> permitir sólo a los llamadores autorizados realizar operaciones <strong>para</strong>las que tengan permisos. System.Security (Espacio <strong>de</strong> nombres) contiene clases que ayudan arealizar estas comprobaciones en las bibliotecas <strong>de</strong> clases que se escriban. El código <strong>de</strong> bibliotecas<strong>de</strong> clases a menudo es código <strong>de</strong> plena confianza o como mínimo <strong>de</strong> alta confianza. Dado que elcódigo <strong>de</strong> bibliotecas <strong>de</strong> clases tiene acceso con bastante frecuencia a recursos protegidos y códigono administrado, cualquier <strong>de</strong>fecto <strong>de</strong>l código representa una grave amenaza a la integridad <strong>de</strong> todoel sistema <strong>de</strong> seguridad. Para minimizar los peligros relacionados con la seguridad, siga lasinstrucciones que se <strong>de</strong>scriben en este tema cuando escriba código <strong>de</strong> bibliotecas <strong>de</strong> clases. Paraobtener más información, vea Escribir bibliotecas <strong>de</strong> clases seguras.Proteger objetos con permisosLos permisos se <strong>de</strong>finen <strong>para</strong> proteger recursos específicos. Una biblioteca <strong>de</strong> clases que realizaoperaciones en recursos protegidos <strong>de</strong>be exigir esta protección Antes <strong>de</strong> realizar alguna acción enla solicitud <strong>de</strong> un recurso protegido, como eliminar un archivo, el código <strong>de</strong> la biblioteca <strong>de</strong> clases<strong>de</strong>be comprobar, en primer lugar, que el llamador tiene permiso (y, normalmente, todos losllamadores, mediante el recorrido <strong>de</strong> pila) <strong>para</strong> eliminar el recurso. Si el llamador tiene el permisoa<strong>de</strong>cuado, permitirá completar la acción. Si el llamador no tiene permiso, la acción no <strong>de</strong>becompletarse y se <strong>de</strong>be provocar una excepción <strong>de</strong> seguridad. La protección se implementa, por logeneral, en el código con una comprobación <strong>de</strong>clarativa o una comprobación imperativa <strong>de</strong> lospermisos apropiados.Es importante que las clases protejan los recursos, no sólo contra el acceso directo, sino tambiéncontra cualquier tipo <strong>de</strong> exposición posible. Por ejemplo, el objeto <strong>de</strong> un archivo almacenado en lacaché es responsable <strong>de</strong> comprobar los permisos <strong>de</strong> lectura <strong>de</strong>l archivo, aunque los datos reales serecuperen <strong>de</strong> una caché en memoria y no se produzca ninguna operación real en el archivo. Esto esasí porque el efecto <strong>de</strong> pasar datos al llamador es el mismo que si el llamador realiza una operación<strong>de</strong> lectura real.Código <strong>de</strong> biblioteca <strong>de</strong> clases <strong>de</strong> plena confianzaEn la mayoría <strong>de</strong> las bibliotecas <strong>de</strong> clases se implementa código <strong>de</strong> plena confianza que encapsulala funcionalidad específica <strong>de</strong> la plataforma como objetos administrados, por ejemplo, las API <strong>de</strong>lsistema o <strong>de</strong> COM. El código <strong>de</strong> plena confianza pue<strong>de</strong> exponer una <strong>de</strong>bilidad en la seguridad <strong>de</strong>todo el sistema. No obstante, esto no <strong>de</strong>be ocurrir si las bibliotecas <strong>de</strong> clases se escribencorrectamente con relación a la seguridad, colocando una alta carga <strong>de</strong> seguridad en un conjunto<strong>de</strong> bibliotecas <strong>de</strong> clases relativamente pequeño; y permitiendo que la parte más extensa <strong>de</strong>l códigoadministrado adquiera la seguridad principal en tiempo <strong>de</strong> ejecución <strong>de</strong> estas bibliotecas <strong>de</strong> clasesprincipales.En un escenario habitual <strong>de</strong> seguridad <strong>de</strong> bibliotecas <strong>de</strong> clases, una clase <strong>de</strong> plena confianzaexpone un recurso protegido mediante un permiso; <strong>para</strong> tener acceso al recurso se utiliza una API<strong>de</strong> código nativo. Un ejemplo típico <strong>de</strong> este tipo <strong>de</strong> recurso es un archivo. La Clase File utiliza una
API nativa <strong>para</strong> realizar operaciones en el archivo, como la eliminación. Para proteger el recurso, serealizan los pasos siguientes.1. Un llamador solicita la eliminación <strong>de</strong>l archivo c:\test.txt llamando al Método File.Delete.2. El método Delete crea un objeto <strong>de</strong> permiso que representa el permiso <strong>de</strong>lete c:\test.txt.3. El código <strong>de</strong> la clase File comprueba todos los llamadores <strong>de</strong> la pila <strong>para</strong> ver si tienen elpermiso solicitado; si no se les ha concedido el permiso, se provoca una excepción <strong>de</strong>seguridad.4. La clase File <strong>de</strong>clara FullTrust <strong>para</strong> llamar al código nativo, porque sus llamadores no tieneneste permiso.5. La clase File utiliza una API nativa <strong>para</strong> realizar la operación <strong>de</strong> eliminación <strong>de</strong>l archivo.6. La clase File <strong>de</strong>vuelve a su llamador, y la solicitud <strong>de</strong> eliminación <strong>de</strong>l archivo se completacorrectamente.Precauciones <strong>para</strong> código <strong>de</strong> alta confianzaEl código <strong>de</strong> una biblioteca <strong>de</strong> clases <strong>de</strong> confianza conce<strong>de</strong> permisos que no están disponibles en elcódigo <strong>de</strong> la mayoría <strong>de</strong> las aplicaciones. A<strong>de</strong>más, un ensamblado pue<strong>de</strong> contener clases que nonecesitan permisos especiales, pero que se conce<strong>de</strong>n porque el ensamblado contiene otras clasesque requieren estos permisos. Estas situaciones pue<strong>de</strong>n exponer una <strong>de</strong>bilidad en la seguridad <strong>de</strong>lsistema. Por tanto, se <strong>de</strong>be tener un cuidado especial cuando se escribe código <strong>de</strong> plena confianzay código <strong>de</strong> alta confianza.Diseñe código <strong>de</strong> confianza <strong>para</strong> que se pueda llamar mediante un código <strong>de</strong> confianza parcial <strong>de</strong>lsistema sin exponer puntos vulnerables <strong>de</strong> seguridad. Generalmente, los recursos se protegenmediante el recorrido <strong>de</strong> la pila <strong>de</strong> todos los llamadores. Si un llamador no tiene suficientespermisos, se bloquea el intento <strong>de</strong> acceso. Sin embargo, siempre que el código <strong>de</strong> confianza<strong>de</strong>clare un permiso, el código tiene la responsabilidad <strong>de</strong> comprobar los permisos requeridos.Normalmente, una aserción <strong>de</strong>be ir seguida <strong>de</strong> una comprobación <strong>de</strong> los permisos <strong>de</strong>l llamadorcomo se ha <strong>de</strong>scrito anteriormente en este tema. A<strong>de</strong>más, se <strong>de</strong>be minimizar el número <strong>de</strong>aserciones <strong>de</strong> permisos <strong>para</strong> reducir el riesgo <strong>de</strong> exposiciones inintencionadas.Al código <strong>de</strong> plena confianza se conce<strong>de</strong> implícitamente el resto <strong>de</strong> los permisos. A<strong>de</strong>más, permiteinfringir las reglas <strong>de</strong> seguridad <strong>de</strong> tipos y <strong>de</strong> uso <strong>de</strong> objetos. In<strong>de</strong>pendientemente <strong>de</strong> la protección<strong>de</strong> recursos, cualquier aspecto <strong>de</strong> la interfaz <strong>de</strong> programación que pueda <strong>de</strong>scifrar la seguridad <strong>de</strong>tipos o permitir el acceso <strong>de</strong> datos que, por lo general, no están disponibles <strong>para</strong> el llamador pue<strong>de</strong>nprovocar problemas en la seguridad.RendimientoLas comprobaciones <strong>de</strong> seguridad incluyen la comprobación <strong>de</strong> los permisos <strong>de</strong> los llamadores enla pila. En función <strong>de</strong> la profundidad <strong>de</strong> la pila, estas operaciones pue<strong>de</strong>n tener un costo muyelevado. Si una operación consiste en realidad en un número <strong>de</strong> acciones en un nivel inferior en elque es necesario realizar comprobaciones <strong>de</strong> seguridad, el rendimiento pue<strong>de</strong> mejorarenormemente si se comprueban los permisos <strong>de</strong> los llamadores una vez y, a continuación, se<strong>de</strong>clara el permiso necesario antes <strong>de</strong> realizar acciones. Esta aserción <strong>de</strong>tendrá la propagación <strong>de</strong>lrecorrido por la pila en un punto <strong>para</strong> efectuar correctamente la comprobación. Normalmente, estátécnica da como resultado una mejora <strong>de</strong>l rendimiento si se realizan tres o más comprobaciones <strong>de</strong>permisos cada vez.
Resumen <strong>de</strong> los problemas <strong>de</strong> seguridad <strong>de</strong> las bibliotecas <strong>de</strong> clases• En las bibliotecas <strong>de</strong> clases que utilizan recursos protegidos se <strong>de</strong>be garantizar que sólo seutilizan estos recursos en los permisos <strong>de</strong> los llamadores.• La aserción <strong>de</strong> permisos se <strong>de</strong>be efectuar sólo cuando es necesario y <strong>de</strong>be ir precedida <strong>de</strong> lasnecesarias comprobaciones <strong>de</strong> permisos.• Para mejorar el rendimiento, agregue operaciones que incluyan comprobaciones <strong>de</strong> seguridad yconsi<strong>de</strong>re la posibilidad <strong>de</strong> utilizar aserciones <strong>para</strong> limitar los recorridos <strong>de</strong> la pila sin poner enpeligro la seguridad.• Tenga en cuenta que un llamador malicioso <strong>de</strong> confianza parcial pue<strong>de</strong> en potencia utilizar unaclase <strong>para</strong> omitir la seguridad.• No dé por supuesto que sólo los llamadores con permisos específicos llamarán al código.• No <strong>de</strong>fina interfaces sin seguridad <strong>de</strong> tipos, pues pue<strong>de</strong>n ser utilizadas <strong>para</strong> omitir la segurida<strong>de</strong>n otros lugares.• No exponga la funcionalidad en una clase que permita a un llamador <strong>de</strong> confianza parcialaprovecharse <strong>de</strong> la confianza superior <strong>de</strong> la clase.
Operaciones <strong>de</strong> ca<strong>de</strong>nas que tengan en cuenta lasreferencias culturales y la seguridadLas operaciones <strong>de</strong> ca<strong>de</strong>na que tienen en cuenta la referencia cultural proporcionada por .NETFramework pue<strong>de</strong>n resultar ventajosas <strong>para</strong> los <strong>programadores</strong> que creen aplicaciones diseñadas<strong>para</strong> mostrar resultados basándose en la referencia cultural. De manera pre<strong>de</strong>terminada, losmétodos que tienen en cuenta las referencias culturales obtienen la referencia cultural que van ausar a partir <strong>de</strong> la propiedad CultureInfo.CurrentCulture <strong>de</strong>l subproceso actual. Por ejemplo, elmétodo String.Compare <strong>de</strong>vuelve un resultado que varía según la referencia cultural, <strong>de</strong>bido a lasdiferencias en criterios <strong>de</strong> or<strong>de</strong>nación y asignaciones <strong>de</strong> mayúsculas y minúsculas usadas por lasdistintas referencias culturales. Sin embargo, las operaciones <strong>de</strong> ca<strong>de</strong>na que tienen en cuenta lareferencia cultural no son siempre el comportamiento más <strong>de</strong>seable. Utilizar operaciones que tienenen cuenta la referencia cultural en escenarios en los que los resultados <strong>de</strong>berían ser in<strong>de</strong>pendientes<strong>de</strong> ésta pue<strong>de</strong> hacer que el código no funcione en referencias culturales con asignacionespersonalizadas <strong>de</strong> mayúsculas y minúsculas y reglas <strong>de</strong> or<strong>de</strong>nación, y provocar problemas <strong>de</strong>seguridad en la aplicación.Las operaciones con ca<strong>de</strong>nas que tengan en cuenta la referencia cultural pue<strong>de</strong>n crear puntosvulnerables en la seguridad si el comportamiento esperado por el programador que escribe unabiblioteca <strong>de</strong> clases difiere <strong>de</strong>l comportamiento real <strong>de</strong> funcionamiento en el equipo en el que seejecuta la operación. Estos cambios <strong>de</strong> comportamiento pue<strong>de</strong>n producirse si se cambia lareferencia cultural o si la referencia cultural <strong>de</strong>l equipo en el que se ejecuta la operación es distinta<strong>de</strong> la que utilizó el programador <strong>para</strong> probar el código.Puntos vulnerables en la seguridad en operaciones conca<strong>de</strong>nas que tengan en cuenta la referencia culturalLas exclusivas reglas <strong>de</strong> asignación <strong>de</strong> mayúsculas y minúsculas <strong>de</strong>l alfabeto turco muestran cómose pue<strong>de</strong> usar una operación que tiene en cuenta la referencia cultural <strong>para</strong> crear un puntovulnerable en la seguridad <strong>de</strong>l código <strong>de</strong> una aplicación. En la mayoría <strong>de</strong> los alfabetos latinos, elcarácter i (Unico<strong>de</strong> 0069) es la versión minúscula <strong>de</strong>l carácter I (Unico<strong>de</strong> 0049). Sin embargo, elalfabeto turco tiene dos versiones <strong>de</strong>l carácter I: una con un punto y otra sin él. En turco, el carácterI (Unico<strong>de</strong> 0049) se consi<strong>de</strong>ra la versión mayúscula <strong>de</strong> un carácter diferente ı (Unico<strong>de</strong>0131). Y el carácter i (Unico<strong>de</strong> 0069) se consi<strong>de</strong>ra la versión minúscula <strong>de</strong> otro carácter İ(Unico<strong>de</strong> 0130). Por consiguiente, una com<strong>para</strong>ción <strong>de</strong> ca<strong>de</strong>nas que no tenga en cuenta lasmayúsculas y minúsculas <strong>de</strong> los caracteres i (Unico<strong>de</strong> 0069) e I (Unico<strong>de</strong> 0049), que tendría éxitoen la mayoría <strong>de</strong> las referencias culturales, fallaría <strong>para</strong> la referencia cultural "tr-TR" (turco <strong>de</strong>Turquía).En el siguiente ejemplo <strong>de</strong> código se muestra cómo una operación String.Compare que no tiene encuenta las mayúsculas y minúsculas, realizada sobre las ca<strong>de</strong>nas "FILE" y "file", obtiene resultadosdistintos <strong>de</strong>pendiendo <strong>de</strong> la referencia cultural. La com<strong>para</strong>ción <strong>de</strong>vuelve true si la propiedadThread.CurrentCulture se establece como "en-US" (inglés <strong>de</strong> Estados Unidos). La com<strong>para</strong>ción<strong>de</strong>vuelve false si CurrentCulture se establece como "tr-TR" (turco <strong>de</strong> Turquía). Si una aplicaciónadopta una <strong>de</strong>cisión <strong>de</strong> importancia basándose en el resultado <strong>de</strong> esta operación String.Compare,el resultado <strong>de</strong> esa <strong>de</strong>cisión podría verse trastocado si se cambia el valor <strong>de</strong> CurrentCulture.[Visual Basic]Imports System
Imports System.GlobalizationImports System.ThreadingPublic Class TurkishISamplePublic Shared Sub Main()' Set the CurrentCulture property to English in the U.S.Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")Console.WriteLine("Culture = {0}", _Thread.CurrentThread.CurrentCulture.DisplayName)Console.WriteLine("(file == FILE) = {0}", String.Compare("file", _"FILE", True) = 0)' Set the CurrentCulture property to Turkish in Turkey.Thread.CurrentThread.CurrentCulture = New CultureInfo("tr-TR")Console.WriteLine("Culture = {0}", _Thread.CurrentThread.CurrentCulture.DisplayName)Console.WriteLine("(file == FILE) = {0}", String.Compare("file", _"FILE", True) = 0)End SubEnd Class[C#]using System;using System.Globalization;using System.Threading;public class TurkishISample{public static void Main(){// Set the CurrentCulture property to English in the U.S.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");Console.WriteLine("Culture = {0}",Thread.CurrentThread.CurrentCulture.DisplayName);Console.WriteLine("(file == FILE) = {0}", (string.Compare("file","FILE", true) == 0));
}// Set the CurrentCulture property to Turkish in Turkey.Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");Console.WriteLine("Culture ={0}",Thread.CurrentThread.CurrentCulture.DisplayName);Console.WriteLine("(file == FILE) = {0}", (string.Compare("file","FILE", true) == 0));}Los siguientes resultados en la consola muestran cómo éstos pue<strong>de</strong>n cambiar en función <strong>de</strong> lareferencia cultural, ya que la com<strong>para</strong>ción <strong>de</strong> i e I que no tiene en cuenta las mayúsculas yminúsculas se evalúa como true <strong>para</strong> la referencia cultural "en-US" y como false <strong>para</strong> la referenciacultural "tr-TR".Culture = English (United States)(file == FILE) = TrueCulture = Turkish (Turkey)(file == FILE) = FalsePara obtener más información sobre reglas <strong>de</strong> or<strong>de</strong>nación y asignaciones <strong>de</strong> mayúsculas yminúsculas personalizadas que puedan provocar incoherencias similares, vea Asignacionespersonalizadas <strong>de</strong> mayúsculas y minúsculas, y reglas <strong>de</strong> or<strong>de</strong>nación.Especificar la referencia cultural explícitamente enoperaciones <strong>de</strong> ca<strong>de</strong>nasEl hecho <strong>de</strong> si las operaciones <strong>de</strong> ca<strong>de</strong>nas <strong>de</strong>ben tener en cuenta las referencias culturales o no<strong>de</strong>pen<strong>de</strong> <strong>de</strong> cómo use los resultados la aplicación. Las operaciones <strong>de</strong> ca<strong>de</strong>nas que muestran losresultados al usuario final <strong>de</strong>berían normalmente tener en cuenta las referencias culturales. Porejemplo, si una aplicación muestra al usuario una lista or<strong>de</strong>nada <strong>de</strong> ca<strong>de</strong>nas localizadas en uncuadro <strong>de</strong> lista, <strong>de</strong>bería realizarse una or<strong>de</strong>nación que tuviera en cuenta las referencias culturales.Los resultados <strong>de</strong> las operaciones <strong>de</strong> ca<strong>de</strong>nas que se usan internamente no <strong>de</strong>berían normalmentetener en cuenta las referencias culturales. En general, si está trabajando con nombres <strong>de</strong> archivo,formatos <strong>de</strong> persistencia o información simbólica que no se muestra al usuario final, los resultados<strong>de</strong> las operaciones <strong>de</strong> ca<strong>de</strong>nas no <strong>de</strong>berían variar en función <strong>de</strong> la referencia cultural. Por ejemplo,si una aplicación com<strong>para</strong> una ca<strong>de</strong>na <strong>para</strong> <strong>de</strong>terminar si es una etiqueta XML reconocida, lacom<strong>para</strong>ción no <strong>de</strong>bería tener en cuenta las referencias culturales.La mayoría <strong>de</strong> los métodos .NET Framework que realizan operaciones <strong>de</strong> ca<strong>de</strong>nas que tienen encuenta las referencias culturales <strong>de</strong> manera pre<strong>de</strong>terminada, proporcionan sobrecargas <strong>de</strong>l métodoque permiten especificar <strong>de</strong> forma explícita la referencia cultural que hay que usar, pasando unparámetro CultureInfo. Use estas sobrecargas <strong>para</strong> mostrar claramente si se preten<strong>de</strong> que unaoperación <strong>de</strong> ca<strong>de</strong>nas tenga en cuenta las referencias culturales o no. En operaciones que tienenen cuenta las referencias culturales, especifique la propiedad CurrentCulture <strong>para</strong> el parámetroCultureInfo. Para eliminar los puntos <strong>de</strong> seguridad vulnerables producidos por variaciones <strong>de</strong> lareferencia cultural en las reglas <strong>de</strong> or<strong>de</strong>namiento y asignaciones <strong>de</strong> mayúsculas y minúsculas, se<strong>de</strong>ben realizar operaciones que no tengan en cuenta la referencia cultural; <strong>para</strong> ello, basta con
especificar la propiedad CultureInfo.InvariantCulture <strong>para</strong> el parámetro CultureInfo. Esto garantizaque el código se ejecutará en todos los equipos, sin importar su referencia cultural, con el mismocomportamiento que durante las pruebas.Realizar operaciones <strong>de</strong> ca<strong>de</strong>nas que no distinguen entrereferencias culturalesLas siguientes API <strong>de</strong> .NET Framework realizan operaciones <strong>de</strong> ca<strong>de</strong>nas que tienen en cuenta lareferencia cultural <strong>de</strong> forma pre<strong>de</strong>terminada. Cuando se utilicen estas API, se <strong>de</strong>berá usar siemprela sobrecarga <strong>de</strong> métodos o el constructor <strong>de</strong> clases que permita especificar <strong>de</strong> forma explícita lareferencia cultural que se tiene que usar. Para <strong>de</strong>terminar si se <strong>de</strong>be especificar CurrentCulture(los resultados tienen en cuenta la referencia cultural) o InvariantCulture (los resultados no tienenen cuenta la referencia cultural), siga las pautas <strong>de</strong>scritas en Especificar la referencia culturalexplícitamente en operaciones <strong>de</strong> ca<strong>de</strong>nas.String.Compare (Método)String.CompareTo (Método)String.ToUpper (Método)String.ToLower (Método)Char.ToUpper (Método)Char.ToLower (método)CaseInsensitiveComparer (Clase) |CaseInsensitiveHashCo<strong>de</strong>Provi<strong>de</strong>r (Clase) |SortedList (Clase) |ArrayList.Sort (Método) |CollectionsUtil.CreateCaseInsensitiveHashTable (Método) |Array.Sort (Método) |Array.BinarySearch (Método) |System.Text.RegularExpressions (Espacio <strong>de</strong> nombres)Los temas siguientes proporcionan más información sobre estas API y ejemplos que muestrancómo usarlas correctamente <strong>para</strong> obtener resultados que no tengan en cuenta las referenciasculturales:• Realizar com<strong>para</strong>ciones <strong>de</strong> ca<strong>de</strong>nas que no tienen en cuenta las referencias culturales <strong>de</strong>scribecómo utilizar los métodos String.Compare y String.CompareTo <strong>para</strong> llevar a cabocom<strong>para</strong>ciones <strong>de</strong> ca<strong>de</strong>nas que no tengan en cuenta las referencias culturales.• Realizar cambios <strong>de</strong> mayúsculas y minúsculas que no tienen en cuenta las referenciasculturales <strong>de</strong>scribe cómo utilizar los métodos String.ToUpper, String.ToLower, Char.ToUppery Char.ToLower <strong>para</strong> llevar a cabo cambios <strong>de</strong> mayúsculas y minúsculas que no tengan encuenta las referencias culturales.• Realizar operaciones <strong>de</strong> ca<strong>de</strong>nas que no tienen en cuenta las referencias culturales encolecciones <strong>de</strong>scribe cómo utilizar la clase CaseInsensitiveComparer, la claseCaseInsensitiveHashCo<strong>de</strong>Provi<strong>de</strong>r, la clase SortedList, el método ArrayList.Sort y el
método CollectionsUtil.CreateCaseInsensitiveHashtable <strong>para</strong> llevar a cabo operaciones encolecciones que no tengan en cuenta las referencias culturales.• Realizar com<strong>para</strong>ciones <strong>de</strong> ca<strong>de</strong>nas que no tienen en cuenta las referencias culturales enmatrices <strong>de</strong>scribe cómo utilizar los métodos Array.Sort y Array.BinarySearch <strong>para</strong> llevar acabo operaciones en matrices que no tengan en cuenta las referencias culturales.• Realizar operaciones que no tienen en cuenta las referencias culturales en el espacio <strong>de</strong>nombres <strong>de</strong>scribe cómo llevar a cabo operaciones que no tengan en cuenta las referenciasculturales usando métodos en el espacio <strong>de</strong> nombres RegularExpressions.Resumen <strong>de</strong> las instrucciones <strong>de</strong> uso <strong>de</strong> ca<strong>de</strong>nas quetengan en cuenta las referencias culturalesAl realizar operaciones en el código <strong>de</strong> la biblioteca <strong>de</strong> clases con ca<strong>de</strong>nas que tengan en cuentalas referencias culturales, tenga en cuenta las pautas siguientes:• Pase <strong>de</strong> forma explícita la información sobre la referencia cultural a todas las operaciones quetengan en cuenta las referencias culturales. Especifique CultureInfo.CurrentCulture si <strong>de</strong>seaun comportamiento que tenga en cuenta las referencias culturales. EspecifiqueCultureInfo.InvariantCulture si <strong>de</strong>sea un comportamiento que no tenga en cuenta lasreferencias culturales.• Use el método String.Compare en lugar <strong>de</strong>l método String.CompareTo. El métodoString.Compare clarifica si se <strong>de</strong>sea que una operación tenga en cuenta o no las referenciasculturales. Para examinar ejemplos <strong>de</strong> código que muestra cómo usar el métodoString.Compare, vea Realizar com<strong>para</strong>ciones <strong>de</strong> ca<strong>de</strong>nas que no tienen en cuenta lasreferencias culturales.• Proporcione medios <strong>para</strong> personalizar la referencia cultural <strong>de</strong> todos los componentes que useninternamente operaciones que no tengan en cuenta la referencia cultural.<strong>Instrucciones</strong> <strong>de</strong> diseño <strong>de</strong> subprocesosEn las reglas siguientes se <strong>de</strong>scriben las instrucciones <strong>de</strong> diseño <strong>de</strong> implementación <strong>de</strong>subprocesos:• Evite proporcionar métodos estáticos que modifiquen el estado estático. En los escenarios <strong>de</strong>servidor habituales, el estado estático se comparte entre solicitu<strong>de</strong>s, lo que significa que variossubprocesos pue<strong>de</strong>n ejecutar ese código al mismo tiempo. Esto abre la posibilidad <strong>de</strong> erroresen el subproceso. Consi<strong>de</strong>re la posibilidad <strong>de</strong> utilizar un mo<strong>de</strong>lo <strong>de</strong> diseño que encapsule losdatos en instancias que no se compartan entre solicitu<strong>de</strong>s.• El estado estático <strong>de</strong>be ser seguro <strong>para</strong> la ejecución <strong>de</strong> subprocesos.• No es necesario que el estado <strong>de</strong> la instancia sea seguro <strong>para</strong> la ejecución <strong>de</strong> subprocesos. Deforma pre<strong>de</strong>terminada, las bibliotecas <strong>de</strong> clases no <strong>de</strong>berían ser seguras <strong>para</strong> la ejecución <strong>de</strong>subprocesos. La agregación <strong>de</strong> bloqueos <strong>para</strong> crear un código seguro <strong>para</strong> la ejecución <strong>de</strong>subprocesos reduce el rendimiento y aumenta la contención <strong>de</strong> bloqueos, con lo que se crea laposibilidad <strong>de</strong> que se produzcan errores en el bloqueo. En los mo<strong>de</strong>los habituales <strong>de</strong>aplicaciones, un solo subproceso ejecuta el código <strong>de</strong> usuario cada vez, lo que minimiza lanecesidad <strong>de</strong> seguridad <strong>para</strong> subprocesos. Por este motivo, las bibliotecas <strong>de</strong> clases <strong>de</strong> .NETFramework no son <strong>de</strong> forma pre<strong>de</strong>terminada seguras <strong>para</strong> la ejecución <strong>de</strong> subprocesos. En loscasos en los que <strong>de</strong>see proporcionar una versión segura <strong>para</strong> ejecutar subprocesos,
proporcione un método Synchronized estático que <strong>de</strong>vuelva una instancia segura <strong>para</strong> laejecución <strong>de</strong> subprocesos <strong>de</strong> un tipo. Para examinar un ejemplo, vea el métodoSystem.Collections.ArrayList.Synchronized y el métodoSystem.Collections.ArrayList.IsSynchronized.• Diseñe la biblioteca teniendo en cuenta la carga <strong>de</strong> ejecución en un escenario <strong>de</strong> servidor. Eviteutilizar bloqueos siempre que sea posible.• Tenga en cuenta las llamadas a métodos en las secciones bloqueadas. Los bloqueos sepue<strong>de</strong>n producir cuando el método estático <strong>de</strong> una clase A llama a los métodos estáticos <strong>de</strong> laclase B y viceversa. Si A y B sincronizan sus métodos estáticos, se producirá un bloqueo.Pue<strong>de</strong> ocurrir que este bloqueo se <strong>de</strong>scubra sólo en condiciones <strong>de</strong> una gran carga <strong>de</strong>subprocesos.• Se pue<strong>de</strong>n producir problemas <strong>de</strong> rendimiento cuando un método estático en la clase A llame aun método estático en la clase A. Si estos métodos no están correctamente factorizados, estoafectará al rendimiento porque habrá una gran cantidad <strong>de</strong> sincronización redundante. El usoexcesivo <strong>de</strong> una sincronización más flexible y <strong>de</strong>tallada pue<strong>de</strong> tener un impacto negativo en elrendimiento. A<strong>de</strong>más, pue<strong>de</strong> tener un impacto muy negativo en la escalabilidad.• Tenga en cuenta los problemas con la instrucción lock (SyncLock en Visual Basic). Resultamás apetecible utilizar la instrucción lock <strong>para</strong> solucionar todos los problemas <strong>de</strong> lossubprocesos. Sin embargo, la Clase System.Threading.Interlocked es mejor <strong>para</strong> lasactualizaciones que <strong>de</strong>ban ser atómicas. Esta clase ejecuta un prefijo lock si no haycontención. En una revisión <strong>de</strong> código, tenga cuidado con instancias como la que se muestra enel ejemplo siguiente.[Visual Basic]SyncLock MemyField += 1End SyncLock[C#]lock(this){}myField++;Si se reemplaza el ejemplo anterior con el siguiente, se mejorará el rendimiento.[Visual Basic]System.Threading.Interlocked.Increment(myField)[C#]System.Threading.Interlocked.Increment(myField);Otro ejemplo es actualizar la variable <strong>de</strong> un tipo <strong>de</strong> objeto sólo si es null (Nothing en VisualBasic). Para actualizar la variable y hacer el código seguro <strong>para</strong> subprocesos, Se pue<strong>de</strong> usar elcódigo siguiente.[Visual Basic]If x Is Nothing Then
SyncLock MeIf x Is Nothing Thenx = yEnd IfEnd SyncLockEnd If[C#]if (x == null){}lock (this){}if (x == null){}x = y;Se pue<strong>de</strong> mejorar el rendimiento <strong>de</strong>l ejemplo anterior cambiando el código por lo siguiente.[Visual Basic]System.Threading.Interlocked.CompareExchange(x, y, Nothing)[C#]System.Threading.Interlocked.CompareExchange(ref x, y, null);• Evite la necesidad <strong>de</strong> sincronización siempre que sea posible. En las rutas <strong>de</strong> mucho tráfico, esmejor evitar la sincronización. Algunas veces se pue<strong>de</strong> ajustar el algoritmo <strong>para</strong> tolerar lascondiciones <strong>de</strong> anticipación, en vez <strong>de</strong> eliminarlas.
<strong>Instrucciones</strong> <strong>para</strong> programación asincrónicaLa programación asincrónica es una función compatible con muchas áreas <strong>de</strong> Common LanguageRuntime, como el Entorno remoto, ASP.NET y los formularios Windows Forms. La programaciónasincrónica es un concepto principal en .NET Framework. En este tema se <strong>de</strong>scribe el mo<strong>de</strong>lo <strong>de</strong>diseño <strong>de</strong> programación asincrónica.La filosofía <strong>de</strong> estas instrucciones es como sigue:• El cliente <strong>de</strong>be <strong>de</strong>cidir si una <strong>de</strong>terminada llamada <strong>de</strong>be ser asincrónica.• No es necesario una programación adicional en el servidor <strong>para</strong> que sea compatible con elcomportamiento asincrónico <strong>de</strong>l cliente. El motor <strong>de</strong> tiempo <strong>de</strong> ejecución <strong>de</strong>be ser capaz <strong>de</strong>administrar la diferencia entre el cliente y el servidor. En consecuencia, se evita la situación enla que el servidor tiene que implementar IDispatch y realizar una gran cantidad <strong>de</strong> trabajo <strong>para</strong>ser compatible con la invocación dinámica <strong>de</strong>l cliente.• El servidor pue<strong>de</strong> elegir admitir explícitamente el comportamiento asincrónico <strong>para</strong> implementareste comportamiento <strong>de</strong> forma más eficiente que con una arquitectura general; o <strong>para</strong> admitir elcomportamiento asincrónico <strong>de</strong> los clientes. Es conveniente que estos servidores sigan elmo<strong>de</strong>lo <strong>de</strong> diseño <strong>de</strong>scrito en este documento <strong>para</strong> exponer operaciones asincrónicas.• Se <strong>de</strong>be exigir la seguridad <strong>de</strong> tipos.• El tiempo <strong>de</strong> ejecución proporciona los servicios necesarios <strong>para</strong> admitir el mo<strong>de</strong>lo <strong>de</strong>programación asincrónica. Entre estos servicios se incluyen los siguientes:• Tipos <strong>de</strong> sincronización primitivos, como las secciones críticas e instanciasRea<strong>de</strong>rWriterLock.• Construcciones <strong>de</strong> sincronización, como contenedores que sean compatibles con el métodoWaitForMultipleObjects.• Grupos <strong>de</strong> subprocesos.• Exposición a la infraestructura subyacente, como objetos Message y ThreadPool.
Mo<strong>de</strong>lo <strong>de</strong> diseño <strong>para</strong> programación asincrónicaEn el siguiente ejemplo <strong>de</strong> código se muestra una clase <strong>de</strong> servidor que factoriza un número.[Visual Basic]Public Class PrimeFactorizerPublic Function Factorize(factorizableNum As Long, ByRef primefactor1As Long, ByRef primefactor2 As Long) As Booleanprimefactor1 = 1primefactor2 = factorizableNum' Factorize using a low-tech approach.Dim i As IntegerFor i = 2 To factorizableNum - 1If 0 = factorizableNum Mod i Thenprimefactor1 = iprimefactor2 = factorizableNum / iExit ForEnd IfNext iIf 1 = primefactor1 ThenReturn FalseElseReturn TrueEnd IfEnd FunctionEnd Class[C#]public class PrimeFactorizer{public bool Factorize(long factorizableNum,ref long primefactor1,ref long primefactor2){primefactor1 = 1;primefactor2 = factorizableNum;
}}// Factorize using a low-tech approach.for (int i=2;i
Create a <strong>de</strong>legate on the Factorize method on the Factorizer.FactorizingDelegate fd = new FactorizingDelegate(pf.Factorize);El compilador emitirá la clase FactorizingAsyncDelegate siguiente <strong>de</strong>spués <strong>de</strong> analizar la<strong>de</strong>finición en la primera línea <strong>de</strong>l ejemplo anterior. Esta clase generará los métodos BeginInvoke yEndInvoke.[Visual Basic]Public Class FactorizingAsyncDelegateInherits DelegatePublic Function Invoke(factorizableNum As Long, ByRef primefactor1 AsLong, ByRef primefactor2 As Long) As BooleanEnd Function' Supplied by the compiler.Public Function BeginInvoke(factorizableNum As Long, ByRef primefactor1As Long, ByRef primefactor2 As Long, cb As AsyncCallback,AsyncState As Object) AsIasyncResultEnd Function' Supplied by the compiler.Public Function EndInvoke(ByRef primefactor1 As Long, ByRefprimefactor2 As Long, ar As IAsyncResult) As BooleanEnd Function[C#]public class FactorizingAsyncDelegate : Delegate{public bool Invoke(ulong factorizableNum,ref ulong primefactor1, ref ulong primefactor2);// Supplied by the compiler.public IAsyncResult BeginInvoke(ulong factorizableNum,ref unsigned long primefactor1,ref unsigned long primefactor2, AsyncCallback cb,Object AsyncState);
}// Supplied by the compiler.public bool EndInvoke(ref ulong primefactor1,ref ulong primefactor2, IAsyncResult ar);La interfaz utilizada como parámetro <strong>de</strong>l <strong>de</strong>legado en el siguiente ejemplo <strong>de</strong> código se <strong>de</strong>fine en labiblioteca <strong>de</strong> clases <strong>de</strong> .NET Framework. Para obtener más información, vea Interfaz IAsyncResult.[Visual Basic]Delegate Function AsyncCallback(ar As IAsyncResult)' Returns true if the asynchronous operation has been completed.Public Interface IasyncResult' Handle to block on for the results.ReadOnly Property IsCompleted() As Boolean' Get accessor implementation goes here.End Property' Caller can use this to wait until operation is complete.ReadOnly Property AsyncWaitHandle() As WaitHandle' Get accessor implementation goes here.End Property' The <strong>de</strong>legate object for which the async call was invoked.ReadOnly Property AsyncObject() As [Object]' Get accessor implementation goes here.End Property' The state object passed in through BeginInvoke.ReadOnly Property AsyncState() As [Object]' Get accessor implementation goes here.End Property' Returns true if the call completed synchronously.ReadOnly Property CompletedSynchronously() As Boolean' Get accessor implementation goes here.End Property
[C#]End Interfacepublic <strong>de</strong>legate AsyncCallback (IAsyncResult ar);public interface IAsyncResult{// Returns true if the asynchronous operation has completed.bool IsCompleted { get; }// Caller can use this to wait until operation is complete.WaitHandle AsyncWaitHandle { get; }// The <strong>de</strong>legate object for which the async call was invoked.Object AsyncObject { get; }// The state object passed in through BeginInvoke.Object AsyncState { get; }}// Returns true if the call completed synchronously.bool CompletedSynchronously { get; }Observe que el objeto que implementa IAsyncResult (Interfaz) <strong>de</strong>be ser un objeto temporizador <strong>de</strong>espera cuya primitiva <strong>de</strong> sincronización subyacente <strong>de</strong>be marcarse una vez cancelada o realizadala llamada. Esto permite al cliente esperar a que se complete la llamada, en vez <strong>de</strong> hacer unson<strong>de</strong>o. El tiempo <strong>de</strong> ejecución proporciona un número <strong>de</strong> objetos temporizador <strong>de</strong> espera quereflejan las primitivas <strong>de</strong> sincronización <strong>de</strong> Win32, como ManualResetEvent, AutoResetEvent yMutex. También proporciona métodos compatibles con la espera <strong>para</strong> que los objetos <strong>de</strong>sincronización se marquen con la semántica "any" o "all". Estos métodos reconocen el contexto<strong>para</strong> evitar bloqueos.El método Cancel es una solicitud <strong>para</strong> cancelar el procesamiento <strong>de</strong>l método, una vez finalizado eltiempo <strong>de</strong> espera seleccionado. Observe que sólo se recomienda una solicitud <strong>de</strong>l cliente y <strong>de</strong>lservidor <strong>para</strong> tenerlo en cuenta. A<strong>de</strong>más, el cliente no <strong>de</strong>be asumir que el servidor ha <strong>de</strong>tenido lasolicitud <strong>de</strong> procesamiento totalmente, una vez recibida la notificación <strong>de</strong> cancelación <strong>de</strong>l método.Es <strong>de</strong>cir, se recomienda al cliente no <strong>de</strong>struir recursos como objetos file, ya que se pue<strong>de</strong>n estarutilizando en el servidor. La propiedad IsCanceled se establecerá en true si se cancela la llamada,y la propiedad IsCompleted se establecerá en true <strong>de</strong>spués <strong>de</strong> que el servidor complete elprocesamiento <strong>de</strong> la llamada. Una vez que el servidor establece la propiedad IsCompleted comotrue, el servidor no pue<strong>de</strong> utilizar ninguno <strong>de</strong> los recursos proporcionados por el cliente fuera <strong>de</strong> lasemántica <strong>de</strong> uso compartido acordada. De este modo, es seguro <strong>para</strong> el cliente <strong>de</strong>struir losrecursos, una vez que la propiedad IsCompleted <strong>de</strong>vuelve el valor true.
La propiedad Server <strong>de</strong>vuelve el objeto <strong>de</strong> servidor proporcionado por IAsyncResult.En el siguiente código <strong>de</strong> ejemplo se muestra el mo<strong>de</strong>lo <strong>de</strong> programación en el cliente <strong>para</strong> invocaral método Factorize <strong>de</strong> forma asincrónica.[C#]public class ProcessFactorizeNumber{private long _ulNumber;public ProcessFactorizeNumber(long number){_ulNumber = number;}[OneWayAttribute()]public void FactorizedResults(IAsyncResult ar){long factor1=0, factor2=0;// Extract the <strong>de</strong>legate from the AsynchResult.FactorizingAsyncDelegate fd =(FactorizingAsyncDelegate) ((AsyncResult)ar).AsyncDelegate;// Obtain the result.fd.EndInvoke(ref factor1, ref factor2, ar);}}// Output the results.Console.Writeline("On CallBack: Factors of {0} : {1} {2}",_ulNumber, factor1, factor2);// Async Variation 1.// The ProcessFactorizeNumber.FactorizedResults callback function// is called when the call completes.public void FactorizeNumber1(){// Client co<strong>de</strong>.
PrimeFactorizer pf = new PrimeFactorizer();FactorizingAsyncDelegate fd = new FactorizingAsyncDelegate (pf.Factorize);long factorizableNum = 1000589023, temp=0;// Create an instance of the class that// will be called when the call completes.ProcessFactorizedNumber fc =new ProcessFactorizedNumber(factorizableNum);// Define the AsyncCallback <strong>de</strong>legate.AsyncCallbackDelegate cb = new AsyncCallback(fc.FactorizedResults);// Any object can be the state object.Object state = new Object();// Asynchronously invoke the Factorize method on pf.// Note: If you have pure out <strong>para</strong>meters, you do not need the// temp variable.IAsyncResult ar = fd.BeginInvoke(factorizableNum, ref temp, ref temp,cb, state);// Proceed to do other useful work.// Async Variation 2.// Waits for the result.// Asynchronously invoke the Factorize method on pf.// Note: If you have pure out <strong>para</strong>meters, you do not need// the temp variable.public void FactorizeNumber2(){// Client co<strong>de</strong>.PrimeFactorizer pf = new PrimeFactorizer();FactorizingAsyncDelegate fd = new FactorizingAsyncDelegate (pf.Factorize);long factorizableNum = 1000589023, temp=0;// Create an instance of the class// to be called when the call completes.
ProcessFactorizedNumber fc =new ProcessFactorizedNumber(factorizableNum);// Define the AsyncCallback <strong>de</strong>legate.AsyncCallback cb = new AsyncCallback(fc.FactorizedResults);// Any object can be the state object.Object state = new Object();// Asynchronously invoke the Factorize method on pf.IAsyncResult ar = fd.BeginInvoke(factorizableNum, ref temp, ref temp,null, null);ar.AsyncWaitHandle.WaitOne(10000, false);if(ar.IsCompleted){int factor1=0, factor2=0;// Obtain the result.fd.EndInvoke(ref factor1, ref factor2, ar);}}// Output the results.Console.Writeline("Sequential : Factors of {0} : {1} {2}",factorizableNum, factor1, factor2);Observe que si FactorizeCallback es una clase enlazada a un contexto que requiere un contextosincronizado o <strong>de</strong> afinidad <strong>de</strong> subprocesos, la función <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada se enviará a través<strong>de</strong> la infraestructura <strong>de</strong>l distribuidor <strong>de</strong> contextos. Es <strong>de</strong>cir, la función <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada sepue<strong>de</strong> ejecutar <strong>de</strong> forma asincrónica con respecto al llamador <strong>de</strong> estos contextos. Ésta es lasemántica <strong>de</strong> un calificador unidireccional <strong>de</strong> firmas <strong>de</strong> métodos. Estas llamadas al método sepue<strong>de</strong>n ejecutar <strong>de</strong> forma sincrónica o asincrónica con respecto al llamador, y el llamador no pue<strong>de</strong>suponer nada sobre la finalización <strong>de</strong> este tipo <strong>de</strong> llamada cuando se <strong>de</strong>vuelve el control <strong>de</strong>ejecución.A<strong>de</strong>más, si se llama a EndInvoke antes <strong>de</strong> completar la operación asincrónica, se bloqueará elllamador. Llamar una segunda vez con igual AsyncResult queda sin <strong>de</strong>finir.
Resumen <strong>de</strong>l mo<strong>de</strong>lo <strong>de</strong> diseño <strong>para</strong> programación asincrónicaEl servidor divi<strong>de</strong> una operación asincrónica en dos partes lógicas: la parte que acepta la entrada<strong>de</strong>l cliente e inicia la operación asincrónica y la parte que proporciona los resultados <strong>de</strong> la operaciónasincrónica <strong>para</strong> el cliente. A<strong>de</strong>más <strong>de</strong> la entrada necesaria <strong>para</strong> la operación asincrónica, laprimera parte también acepta un objeto AsyncCallbackDelegate que se llama <strong>de</strong>spués <strong>de</strong>completar la operación asincrónica. La primera parte <strong>de</strong>vuelve un objeto temporizador <strong>de</strong> esperaque implementa la interfaz IAsyncResult utilizada por el cliente <strong>para</strong> <strong>de</strong>terminar el estado <strong>de</strong> laoperación asincrónica. Normalmente, el servidor también utiliza el objeto temporizador <strong>de</strong> esperaque ha <strong>de</strong>vuelto al cliente <strong>para</strong> mantener cualquier estado asociado con la operación asincrónica. Elcliente utiliza la segunda parte <strong>para</strong> obtener los resultados <strong>de</strong> la operación asincrónicasuministrando el objeto temporizador <strong>de</strong> espera.Cuando se inician las operaciones asincrónicas, el cliente pue<strong>de</strong> suministrar el <strong>de</strong>legado <strong>de</strong> lafunción <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada, o bien no suministrarlo.Las siguientes opciones están disponibles en el cliente <strong>para</strong> completar las operacionesasincrónicas:• Son<strong>de</strong>e el objeto IAsyncResult <strong>de</strong>vuelto <strong>para</strong> su finalización.• Intente completar la operación prematuramente, que estará bloqueada hasta que la operaciónse complete.• Espere en el objeto IAsyncResult. La diferencia entre esta opción y la opción anterior es quese pue<strong>de</strong>n utilizar tiempos <strong>de</strong> espera periódicamente <strong>para</strong> volver a tomar el control.• Complete la operación <strong>de</strong>ntro <strong>de</strong> la rutina <strong>de</strong> la función <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada.Un escenario en el que los métodos <strong>de</strong> lectura y escritura asincrónico y sincrónico son convenienteses el uso <strong>de</strong> la entrada/salida <strong>de</strong> archivos. En el ejemplo siguiente se ilustra el mo<strong>de</strong>lo <strong>de</strong> diseñoque muestra cómo el objeto File implementa las operaciones <strong>de</strong> lectura y escritura.[Visual Basic]Public Class File' Other methods for this class go here.' Synchronous read method.Function Read(buffer() As [Byte], NumToRead As Long) As Long' Asynchronous read method.Function BeginRead(buffer() As [Byte], NumToRead As Long, cb AsAsyncCallbackDelegate) As IAsyncResultFunction EndRead(ar As IAsyncResult) As Long' Synchronous write method.Function Write(buffer() As [Byte], NumToWrite As Long) As Long' Asynchrnous write method.
Function BeginWrite(buffer() As [Byte], NumToWrite As Long, cb AsAsyncCallbackDelegate) As IAsyncResultFunction EndWrite(ar As IAsyncResult) As LongEnd Class[C#]public class File{// Other methods for this class go here.// Synchronous read method.long Read(Byte[] buffer, long NumToRead);// Asynchronous read method.IAsyncResult BeginRead(Byte[] buffer, long NumToRead,AsyncCallbackDelegate cb);long EndRead(IAsyncResult ar);// Synchronous write method.long Write(Byte[] buffer, long NumToWrite);// Asynchrnous write method.IAsyncResult BeginWrite(Byte[] buffer, long NumToWrite,AsyncCallbackDelegate cb);}long EndWrite(IAsyncResult ar);El cliente no pue<strong>de</strong> asociar fácilmente el estado con una operación asincrónica dada sin <strong>de</strong>finir unnuevo <strong>de</strong>legado <strong>de</strong> la función <strong>de</strong> <strong>de</strong>volución <strong>de</strong> llamada <strong>para</strong> cada operación. Esto se pue<strong>de</strong>solucionar haciendo que los métodos Begin, como BeginWrite, adopten un parámetro <strong>de</strong>l objetoadicional que represente el estado y que se captura en la interfaz IAsyncResult.