Está en la página 1de 119

1

Instrucciones de diseño para programadores de
bibliotecas de clases
El entorno administrado de .NET Framework permite a los programadores mejorar el modelo de
programación para hacerlo compatible con una amplia gama de funcionalidades. Las instrucciones
de diseño de .NET Framework tienen como finalidad fomentar la coherencia y la previsibilidad en las
API públicas al habilitar la integración entre lenguajes y el Web. Es muy importante seguir estas
instrucciones de diseño cuando se programan las clases y los componentes que extienden .NET
Framework. El diseño incoherente influye de un modo desfavorable en la productividad de los
programadores. Los complementos y las herramientas de desarrollo pueden convertir algunas de
estas instrucciones en reglas preceptivas de hecho y reducir el valor de los componentes que no las
cumplen. Los componentes que no se ajustan a estas instrucciones de diseño funcionarán, aunque
no lo harán con todo su potencial.
Con estas instrucciones se pretende ayudar a los diseñadores de bibliotecas de clases a
comprender las ventajas y las desventajas entre las distintas soluciones. Puede que haya
situaciones en las que un buen diseño de bibliotecas requiera saltarse estas instrucciones de
diseño. Estos casos no son lo habitual y es importante que una decisión de este tipo esté
adecuadamente justificada. En esta sección se proporcionan instrucciones de uso y de
nomenclatura para los tipos de .NET Framework, así como instrucciones de implementación de
modelos de diseño comunes.

2

Relación con el sistema de tipos común y con
Common Language Specification
El Sistema de tipos común es el modelo que define las reglas que se siguen en Common Language
Runtime para declarar, utilizar y administrar tipos. Este sistema establece un marco de trabajo que
permite la integración entre lenguajes, la seguridad de tipos y ejecutar código de alto rendimiento.
Es la materia prima a partir de la cual se pueden crear bibliotecas de clases.
Common Language Specification (CLS) define un conjunto de reglas comprobables mediante
programación que determina la interoperación de los tipos creados en distintos lenguajes de
programación. La selección de CLS es un modo excelente de garantizar la interoperación entre
lenguajes. Los diseñadores de bibliotecas de clases administradas pueden utilizar CLS para
asegurarse de que las API creadas se pueden llamar desde una amplia gama de lenguajes de
programación. Tenga en cuenta que aunque CLS promueve el diseño correcto de bibliotecas, no lo
impone. Para obtener más información sobre este tema, vea Escribir código compatible con CLS.
Para determinar las funciones que se deben incluir en una biblioteca de clases, siga estos dos
principios orientativos con relación a CLS:
1. Determinar si la función facilita el tipo de desarrollo API adecuado al espacio administrado.
CLS debe ser lo suficientemente completo como para permitir escribir cualquier tipo de
biblioteca administrada. No obstante, si proporciona múltiples formas de realizar una misma
tarea, puede desorientar al usuario de la biblioteca de clases a la hora de decidir sobre la
utilización y el diseño correctos. Por ejemplo, si incluye construcciones seguras y
construcciones no seguras los usuarios deberán elegir entre estas dos opciones. Por
consiguiente, CLS fomenta el uso correcto porque ofrece sólo construcciones con seguridad de
tipos.
2. Determinar si para el compilador puede ser difícil exponer la función.
Todos los lenguajes de programación requerirán algún tipo de modificación para que se
adapten al tiempo de ejecución y al sistema de tipos común. Sin embargo, para que los
programadores puedan hacer que un lenguaje sea compatible con CLS, no necesitan crear una
gran cantidad de trabajo adicional. El objetivo de CLS es ser lo más pequeño posible a la vez
que ofrece un extenso conjunto de funciones y tipos de datos.
3

Instrucciones de nomenclatura
La existencia de un modelo de nomenclatura coherente, es uno de los elementos más importantes
en cuanto a previsibilidad y capacidad de descubrimiento en una biblioteca de clases. El uso y el
conocimiento generalizados de estas instrucciones de nomenclatura debería eliminar la mayoría de
las preguntas más frecuentes de los usuarios. En este tema se proporcionan instrucciones de
nomenclatura para los tipos de .NET Framework. En cada tipo, deberá tener en cuenta también
algunas de las reglas generales con relación a los estilos de mayúsculas, distinción entre
mayúsculas y minúsculas y elección de palabras.
4

Estilos de mayúsculas
Utilice las tres convenciones siguientes para poner en mayúsculas los identificadores.
#
Mayúsculas y minúsculas Pascal
La primera letra del identificador y la primera letra de las siguientes palabras concatenadas están en
mayúsculas. El estilo de mayúsculas y minúsculas Pascal se puede utilizar en identificadores de
tres o más caracteres. Por ejemplo:
BackColor
#
Mayúsculas y minúsculas Camel
La primera letra del identificador está en minúscula y la primera letra de las siguientes palabras
concatenadas en mayúscula. Por ejemplo:
backColor
Mayúsculas
Todas las letras del identificador van en mayúsculas. Utilice esta convención sólo para
identificadores que estén formados por dos o menos letras. Por ejemplo:
System.IO
System.Web.UI
Además, puede que sea necesario utilizar mayúsculas en los identificadores para mantener la
compatibilidad con esquemas existentes de símbolos no administrados, donde los caracteres en
mayúsculas se utilizan con frecuencia en valores de constantes y enumeraciones. En general, estos
símbolos no deben ser visibles fuera del ensamblado en el que se utilizan.
En la tabla siguiente se resumen las reglas de uso de mayúsculas y se proporcionan ejemplos de
los diferentes tipos de identificadores.

Identificador
Uso de
mayúsculas o
minúsculas
Ejemplo
Class Pascal AppDomain
Tipo Enum Pascal ErrorLevel
Valores enum Pascal FatalError
Evento Pascal ValueChange
Clase de
excepciones
Pascal
WebException
Nota Termina siempre con el sufijo Exception.
Campo estático de
sólo lectura
Pascal
RedValue

#
cpconNamingGuidelinesAnchor1
#
cpconNamingGuidelinesCamelCasingAnchor2
5

Interfaz Pascal
IDisposable
Nota Comienza siempre con el prefijo I.
Método Pascal ToString
Espacio de
nombres
Pascal
System.Drawing
Parámetro Camel typeName
Propiedad Pascal BackColor
Campo de
instancia protegido
Camel
redValue
Nota Se utiliza en contadas ocasiones. Es
preferible utilizar una propiedad, en vez de un
campo de instancia protegido.
Campo de
instancia público
Pascal
RedValue
Nota Se utiliza en contadas ocasiones. Es
preferible utilizar una propiedad, en vez de un
campo de instancia público.

6

Distinción de mayúsculas y minúsculas
Para evitar confusiones y garantizar la interoperación entre lenguajes, siga estas reglas con
respecto a la distinción entre mayúsculas y minúsculas:
• No utilice nombres que requieran distinción entre mayúsculas y minúsculas. Los componentes
se deben poder utilizar en los lenguajes que distinguen, y en los que no distinguen, entre
mayúsculas y minúsculas. Los lenguajes que no hacen esta distinción no pueden diferenciar,
dentro del mismo contexto, dos nombres que difieren sólo en el uso de mayúsculas y
minúsculas. Por consiguiente, se debe evitar esta situación en los componentes o clases
creados.
• No debe crear dos espacios de nombres con nombres que difieran sólo en las mayúsculas y
minúsculas. Por ejemplo, un lenguaje que no haga distinción entre mayúsculas y minúsculas no
distingue entre las dos declaraciones siguientes de espacio de nombres.
namespace ee.cummings;
namespace Ee.Cummings;
• No debe crear una función con nombres de parámetros que difieran sólo en las mayúsculas y
minúsculas. El siguiente ejemplo es incorrecto.
void MyFunction(string a, string A)
• No cree un espacio de nombres con nombres de tipos que difieran sólo en las mayúsculas y
minúsculas. En el siguiente ejemplo, Point p y POINT p son nombres de tipo incorrectos ya que
difieren sólo en el uso de las mayúsculas y minúsculas.
System.Windows.Forms.Point p
System.Windows.Forms.POINT p
• No debe crear un tipo con nombres de propiedades que difieran sólo en las mayúsculas y
minúsculas. En el siguiente ejemplo, int Color y int COLOR son nombres de propiedades
incorrectos ya que difieren sólo en el uso de las mayúsculas y minúsculas.
int Color {get, set}
int COLOR {get, set}
• No debe crear un tipo con nombres de métodos que difieran sólo en las mayúsculas y
minúsculas. En el siguiente ejemplo, calculate y Calculate son nombres de métodos incorrectos
ya que difieren sólo en el uso de las mayúsculas y minúsculas.
void calculate()
void Calculate()
7

Abreviaturas
Para evitar confusiones y garantizar la interoperación entre lenguajes, siga estas reglas con
respecto a la utilización de abreviaturas:
• No utilice abreviaturas ni contracciones como parte de nombres de identificadores. Por ejemplo,
utilice GetWindow en vez de GetWin.
• No utilice acrónimos que no estén aceptados en el campo de la informática.
• Si es necesario, utilice acrónimos conocidos para reemplazar nombres en frases largas. Por
ejemplo, utilice UI para interfaz de usuario y OLAP para procesamiento analítico en línea.
• Cuando utilice acrónimos, utilice el estilo de mayúsculas y minúsculas Pascal o Camel en
acrónimos de más de dos caracteres. Por ejemplo, use HtmlButton o htmlButton. Sin embargo,
deberá utilizar mayúsculas en acrónimos que tengan sólo dos caracteres, por ejemplo
System.IO en vez de System.Io.
• No utilice abreviaturas en nombres de identificadores o parámetros. Si tiene que utilizar
abreviaturas, utilice las Mayúsculas y minúsculas Camel en abreviaturas de dos o más
caracteres, aunque esto contradiga la abreviatura estándar de la palabra.
Elección de palabra
Evite utilizar nombres de clases que dupliquen los espacios de nombres utilizados habitualmente en
.NET Framework. Por ejemplo, no utilice ninguno de los nombres siguientes como nombres de
clases: System, Collections, Forms o UI. Vea Biblioteca de clases para obtener una lista de los
espacios de nombres de .NET Framework.
Además, evite utilizar identificadores que entren en conflicto con las siguientes palabras clave.

Addhandler AddressOf Alias And Ansi
As Assembly Auto Base Boolean
ByRef Byte ByVal Call Uso de
mayúsculas o
minúsculas
Catch CBool CByte CChar CDate
CDec CDbl Char CInt Class
CLng CObj Const CShort CSng
CStr CType Date Decimal Declare
Default Delegate Dim Do Double
Each Else ElseIf End Enumeración
Erase Error Evento Exit ExternalSource
False Finalize Finally Float For
Friend Función Get GetType GoTo
Handles If Implements Imports In
8

Inherits Integer Interfaz Is Let
Lib Like Long Loop Me
Mod Module MustInherit MustOverride MyBase
MyClass Namespace Nueva Next Not
Nothing NotInheritable NotOverridable Object On
Opción Opcional Or Overloads Overridable
Overrides ParamArray Preserve Private Property
Protected Public RaiseEvent ReadOnly ReDim
Region REM RemoveHandler Resume Return
Select Set Shadows Shared Short
Single Static Paso Stop String
Structure Sub SyncLock Then Throw
A True Try TypeOf Unicode
Until volatile When While With
WithEvents WriteOnly Xor eval extends
instanceof package var

9

Evitar confusión de nombres de tipos
Los distintos lenguajes de programación utilizan términos diferentes para identificar los tipos
administrados básicos. Los diseñadores de bibliotecas de clases deben evitar utilizar terminología
específica del lenguaje. Siga las reglas que se describen en esta sección para evitar la confusión de
nombres de tipos.
Utilice nombres que describan el significado del tipo, en vez de nombres que describan el tipo. En el
caso poco frecuente de que un parámetro no tenga ningún otro significado semántico aparte del
significado del tipo, utilice un nombre genérico. Por ejemplo, una clase que admite la escritura de
diversidad de tipos de datos en una secuencia puede 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 de métodos con terminología específica del lenguaje, como se muestra en el
siguiente 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);
10

En el caso excepcional de que sea necesario crear un método con un nombre único para cada tipo
básico de datos, utilice nombres de tipos universales. En la tabla siguiente se muestran los nombres
de tipos básicos y las sustituciones universales.

Nombre
de tipo C#
Nombre de
tipo Visual
Basic
Nombre de
tipo JScript
Nombre de
tipo Visual
C++
Representación
Ilasm.exe
Nombre de
tipo
universal
sbyte SByte sByte char int8 SByte
byte Byte byte unsigned char unsigned int8 Byte
short Short short short int16 Int16
ushort UInt16 ushort unsigned
short
unsigned int16 UInt16
int Integer int int int32 Int32
uint UInt32 uint unsigned int unsigned int32 UInt32
long Long long __int64 int64 Int64
ulong UInt64 ulong unsigned
__int64
unsigned int64 UInt64
float Single float float float32 Single
double Double double double float64 Double
bool Boolean boolean bool bool Boolean
char Char char wchar_t char Char
string String string String string String
object Object object Object object Object

Por ejemplo, una clase que admita la lectura de diversidad de tipos de datos en una secuencia
puede tener los siguientes métodos.
[Visual Basic]
ReadDouble()As Double
ReadSingle()As Single
ReadInt64()As Long
ReadInt32()As Integer
ReadInt16()As Short
[C#]
double ReadDouble();
float ReadSingle();
long ReadInt64();
int ReadInt32();
short ReadInt16();
11

Es preferible utilizar el ejemplo anterior, en vez de la alternativa específica de lenguaje que se
muestra a continuación.
[Visual Basic]
ReadDouble()As Double
ReadSingle()As Single
ReadLong()As Long
ReadInteger()As Integer
ReadShort()As Short
[C#]
double ReadDouble();
float ReadFloat();
long ReadLong();
int ReadInt();
short ReadShort();
12

Instrucciones de nomenclatura de espacios de
nombres
Por regla general, en los espacios de nombres se utiliza el nombre de la compañía seguido del
nombre de la tecnología y, opcionalmente, la característica y el diseño como se muestra a
continuación.
CompanyName.TechnologyName[.Feature][.Design]
Por ejemplo:
Microsoft.Media
Microsoft.Media.Design
Al incluir un prefijo en los nombres de espacios de nombres que contengan el nombre de una
compañía o marca de reconocido prestigio, se evita la posibilidad de publicar dos espacios de
nombres que tengan el mismo nombre. Por ejemplo, Microsoft.Office es un prefijo adecuado para
las clases de automatización de Office que proporciona Microsoft.
Utilice un nombre de tecnología reconocida y estable en el segundo nivel de un nombre jerárquico.
Utilice una jerarquía organizativa como base para la jerarquía de espacios de nombres. Asigne un
nombre a un espacio de nombres que contenga los tipos que proporcionan funcionalidades en
tiempo de diseño para un espacio de nombres base con el sufijo .Design. Por ejemplo, el Espacio
de nombres System.Windows.Forms.Design contiene las clases de diseñador y clases relacionadas
para diseñar aplicaciones basadas en System.Windows.Forms.
Un espacio de nombres anidado debe tener una dependencia en los tipos del espacio de nombres
contenedor. Por ejemplo, las clases de System.Web.UI.Design dependen de las clases de
System.Web.UI. No obstante, las clases de System.Web.UI no dependen de las clases en
System.Web.UI.Design.
En los espacios de nombres, se debe utilizar el estilo de Mayúsculas y minúsculas Pascal y separar
los componentes lógicos con puntos, como en Microsoft.Office.PowerPoint. Si la marca utilizada no
emplea la regla de mayúsculas y minúsculas tradicional, siga el sistema que utiliza la marca,
aunque no siga la regla Pascal. Por ejemplo, los espacios de nombres NeXT.WebObjects y
ee.cummings muestran las variaciones correctas de utilización de mayúsculas y minúsculas con
respecto a la regla Pascal.
Utilice los nombres de espacios de nombres en plural, siempre que sea correcto semánticamente.
Por ejemplo, utilice System.Collections en vez de System.Collection. Las excepciones a esta regla
son los nombres y abreviaturas de marcas. Por ejemplo, utilice System.IO en vez de System.IOs.
No utilice el mismo nombre para un espacio de nombres y para una clase. Por ejemplo, no
proporcione un espacio de nombres Debug y una clase Debug.
Por último, tenga en cuenta que el nombre de un espacio de nombres no tiene que ser análogo al
nombre del ensamblado. Por ejemplo, si asigna el nombre MyCompany.MyTechnology.dll a un
ensamblado, no es necesario que contenga un espacio de nombres MyCompany.MyTechnology.
13

Instrucciones de nomenclatura de clases
En las reglas siguientes se describen las instrucciones de nomenclatura de clases:
• Utilice un sustantivo o un sintagma nominal para asignar un nombre a una clase.
• Utilice el estilo de Mayúsculas y minúsculas Pascal.
• Utilice las abreviaturas con moderación.
• No utilice un prefijo de tipo, como C para clase, en un nombre de clase. Utilice, por ejemplo, el
nombre de clase FileStream en vez de CFileStream.
• No utilice el carácter de subrayado (_).
• De vez en cuando, es necesario proporcionar un nombre de clase que comience con la letra I,
aunque la clase no sea una interfaz. Esto es correcto siempre que I sea la primera letra de una
palabra que forme parte del nombre de la clase. Por ejemplo, IdentityStore es un nombre de
clase correcto.
• Cuando sea apropiado, utilice una palabra compuesta en el nombre de una clase derivada. La
segunda parte del nombre de la clase derivada debe ser el nombre de la clase base. Por
ejemplo, ApplicationException es un nombre correcto para una clase derivada de la clase
Exception, pues ApplicationException es una clase de Exception. En esta regla se debe utilizar
la lógica. Por ejemplo, Button es un nombre adecuado para una clase derivada de Control.
Aunque un botón es una clase de control, si incluye Control como parte del nombre de una
clase alargaría el nombre innecesariamente.
A continuación, se incluyen algunos ejemplos de clases con nombres correctos:
[Visual Basic]
Public Class FileStream
Public Class Button
Public Class String
[C#]
public class FileStream
public class Button
public class String
14

Instrucciones de nomenclatura de interfaces
En las reglas siguientes se describen las pautas de nomenclatura de interfaces:
• Asigne nombres a interfaces utilizando sustantivos, sintagmas nominales o adjetivos que
describan su comportamiento. Por ejemplo, en el nombre de la interfaz IComponent se utiliza
un sustantivo descriptivo. En el nombre de la interfaz ICustomAttributeProvider se utiliza un
sintagma nominal. En el nombre IPersistable se utiliza un adjetivo.
• Utilice el estilo de Mayúsculas y minúsculas Pascal.
• Utilice las abreviaturas con moderación.
• Incluya un prefijo con la letra I en los nombres de interfaces para indicar que el tipo es una
interfaz.
• Utilice nombres similares cuando defina un par clase/interfaz, donde la clase es una
implementación estándar de la interfaz. Los nombres deben ser distintos sólo en el prefijo I del
nombre de la interfaz.
• No utilice el carácter de subrayado (_).
A continuación, se incluyen algunos ejemplos de interfaces con nombres correctos:
[Visual Basic]
Public Interface IServiceProvider
Public Interface IFormatable
[C#]
public interface IServiceProvider
public interface IFormatable
En el siguiente ejemplo de código se muestra cómo definir la interfaz IComponent y su
implementación estándar, la clase Component.
[Visual Basic]
Public Interface IComponent
' Implementation code goes here.
End Interface
Public Class Component
Implements IComponent
' Implementation code goes here.
End Class
[C#]
public interface IComponent
{// Implementation code goes here.}
public class Component: IComponent
{// Implementation code goes here.}
15

Instrucciones de nomenclatura de atributos
Deberá agregar siempre el sufijo Attribute a las clases de atributos personalizados. A continuación,
se incluye un ejemplo de un nombre correcto de clase de atributo:
[Visual Basic]
Public Class ObsoleteAttribute
[C#]
public class ObsoleteAttribute{}
Instrucciones de nomenclatura de tipos de
enumeración
El tipo de valor de enumeración (Enum) se hereda de la Clase Enum. En las reglas siguientes se
describen las instrucciones de nomenclatura de enumeraciones:
• Utilice el estilo de Mayúsculas y minúsculas Pascal en los nombres de valores y tipos Enum.
• Utilice las abreviaturas con moderación.
• No utilice el sufijo Enum en nombres de tipo Enum.
• Utilice un nombre en singular para la mayoría de los tipos Enum, pero utilice un nombre en
plural para los tipos Enum que son campos de bits.
• Agregue siempre FlagsAttribute a un tipo Enum de campo de bits.
Instrucciones de nomenclatura de campos estáticos
En las reglas siguientes se describen las instrucciones de nomenclatura de campos estáticos:
• Utilice sustantivos, sintagmas nominales o abreviaturas de nombres al asignar nombres a
campos estáticos.
• Utilice el estilo de Mayúsculas y minúsculas Pascal.
• No utilice un prefijo de notación húngara en nombres de campos estáticos.
• Se recomienda utilizar propiedades estáticas en lugar de campos estáticos públicos cada vez
que sea posible.
16

Instrucciones de nomenclatura de parámetros
Es importante seguir estas instrucciones de nomenclatura de parámetros, ya que las herramientas
de diseño visual que proporcionan ayuda contextual y funcionalidad de exploración de clases
muestran en el diseñador los nombres de los parámetros de métodos a los usuarios. En las reglas
siguientes se describen las instrucciones de nomenclatura de parámetros:
• Utilice el estilo de Mayúsculas y minúsculas Camel para los nombres de parámetros.
• Utilice nombres de parámetros descriptivos. Los nombres de parámetros deben ser lo
suficientemente descriptivos como para que el nombre y el tipo del parámetro se puedan utilizar
para determinar su significado en la mayoría de los escenarios. Por ejemplo, las herramientas
de diseño visual que proporcionan ayuda contextual muestran los parámetros de los métodos a
los programadores mientras escriben. Por tanto, los nombres de los parámetros deberían ser lo
suficientemente descriptivos en este escenario como para permitir a los programadores
suministrar los parámetros adecuados.
• Utilice nombres que describan el significado del parámetro, en vez de nombres que describan el
tipo de parámetro. Las herramientas de desarrollo deben proporcionar información descriptiva
sobre el tipo de parámetro. Por tanto, el nombre del parámetro también sirve para describir su
significado. Utilice los nombres de parámetros basados en tipos con moderación y sólo cuando
sea correcto.
• No utilice parámetros reservados. Los parámetros reservados son parámetros privados que se
pueden exponer en futuras versiones, si así se precisa. En vez de esto, si se necesitan más
datos en una versión futura de la biblioteca de clases, agregue una nueva sobrecarga para un
método.
• No incluya un prefijo en los nombres de parámetros con notación húngara de tipo.
A continuación, se incluyen algunos ejemplos de parámetros con nombres correctos:
[Visual Basic]
GetType(typeName As String)As Type
Format(format As String, args() As object)As String
[C#]
Type GetType(string typeName)
string Format(string format, object[] args)
17

Instrucciones de nomenclatura de métodos
En las reglas siguientes se describen las instrucciones de nomenclatura de métodos:
• Utilice verbos o sintagmas verbales al asignar nombres a los métodos.
• Utilice el estilo de Mayúsculas y minúsculas Pascal.
A continuación, se incluyen algunos ejemplos de métodos con nombres correctos:
RemoveAll()
GetCharArray()
Invoke()
Instrucciones de nomenclatura de propiedades
En las reglas siguientes se describen las instrucciones de nomenclatura de propiedades:
• Utilice un sustantivo o un sintagma nominal al asignar nombres a las propiedades.
• Utilice el estilo de 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 subyacente
correspondiente. Por ejemplo, si declara una propiedad con el nombre Color, el tipo de
propiedad también deberá llamarse Color. Vea el ejemplo que se muestra más adelante en este
tema.
En el siguiente ejemplo de código se muestra cómo asignar nombres correctos a propiedades.
[Visual Basic]
Public Class SampleClass
Public Property BackColor As Color
' Code for Get and Set accessors goes here.
End Property
End Class
[C#]
public class SampleClass
{ public Color BackColor
{ // Code for Get and Set accessors goes here. }
}
En el siguiente ejemplo de código se muestra cómo proporcionar una propiedad con el mismo
nombre que el tipo.
[Visual Basic]
Public Enum Color
' Insert code for Enum here.
End Enum
18

Public Class Control
Public Property Color As Color
Get
' Insert code here.
End Get
Set
' Insert code here.
End Set
End Property
End Class
[C#]
public enum Color
{ // Insert code for Enum here.}
public class Control
{ public Color Color
{ get {// Insert code here.}
set {// Insert code here.}
}
}
El siguiente ejemplo de código es incorrecto ya que la propiedad Color es de tipo Integer.
[Visual Basic]
Public Enum Color
' Insert code for Enum here.
End Enum
Public Class Control
Public Property Color As Integer
Get
' Insert code here.
End Get
Set
' Insert code here.
End Set
End Property
End Class
[C#]
public enum Color {// Insert code for Enum here.}
19

public class Control
{
public int Color
{
get {// Insert code here.}
set {// Insert code here.}
}
}
En el ejemplo incorrecto, no es posible hacer referencia a los miembros de la enumeración Color.
Color.Xxx se interpretará como que tiene acceso a un miembro que primero obtiene el valor de la
propiedad Color (tipo Integer en Visual Basic o tipo int en C#) y que, a continuación, tiene acceso a
un miembro de ese valor (que tendrá que ser un miembro de instancia de System.Int32).
Instrucciones de nomenclatura de eventos
En las reglas siguientes se describen las instrucciones de nomenclatura de eventos:
• Utilice el estilo de Mayúsculas y minúsculas Pascal.
• No utilice la notación húngara.
• Utilice un sufijo EventHandler en nombres de controladores de eventos.
• Especifique dos parámetros denominados sender y e. El parámetro sender representa al objeto
que produjo el evento. El parámetro sender es siempre de tipo object, aunque sea posible
utilizar un tipo más específico. El estado asociado con el evento está encapsulado en una
instancia de una clase de eventos denominada e. Use una clase de eventos apropiada y
específica para el tipo de parámetro e.
• Asigne un nombre a la clase de argumentos del evento con el sufijo EventArgs.
• Es conveniente asignar los nombres de eventos con un verbo. Por ejemplo, nombres de
eventos correctos incluyen Clicked, Painting y DroppedDown.
• Utilice un verbo en gerundio para crear un nombre de evento que exprese el concepto de
evento anterior, y un tiempo en pasado para representar un evento posterior. Por ejemplo, un
evento Close que se puede cancelar, debe tener un evento Closing y un evento Closed. No
utilice el modelo de nomenclatura BeforeXxx/AfterXxx.
• No utilice prefijo ni sufijo en la declaración de evento en el tipo. Por ejemplo, utilice Close en vez
de OnClose.
• En general, se debe proporcionar un método protegido denominado OnXxx en los tipos con
eventos que se pueden reemplazar en una clase derivada. Este método debe tener sólo el
parámetro de evento e, ya que el remitente es siempre la instancia del tipo.
En el ejemplo siguiente se muestra un controlador de eventos con nombre y parámetros correctos.
[Visual Basic]
Public Delegate Sub MouseEventHandler(sender As Object, e As MouseEventArgs)
[C#]
20

public delegate void MouseEventHandler(object sender, MouseEventArgs e);
En el siguiente ejemplo se muestra una clase de argumentos de evento con nombre correcto.
[Visual Basic]
Public Class MouseEventArgs
Inherits EventArgs
Dim x As Integer
Dim y As Integer

Public Sub New MouseEventArgs(x As Integer, y As Integer)
me.x = x
me.y = y
End Sub

Public Property X As Integer
Get
Return x
End Get
End Property

Public Property Y As Integer
Get
Return y
End Get
End Property
End 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; } }
}
21

Instrucciones de uso de miembros de clases
En este tema se proporcionan las instrucciones de utilización de miembros de clases en las
bibliotecas de clases.
Instrucciones de uso de propiedades
Determina si es más adecuado utilizar una propiedad o un método en función de las necesidades.
Para obtener más información sobre la elección entre propiedades o métodos, vea Propiedades y
métodos.
Elija un nombre para la propiedad siguiendo las Instrucciones de nomenclatura de propiedades
recomendadas.
Cuando tenga acceso a una propiedad mediante el descriptor de acceso set, conserve el valor de la
propiedad antes de cambiarlo. De este modo, se garantiza que los datos no se perderán si el
descriptor de acceso set inicia una excepción.
Problemas de estado de propiedades
Permita que las propiedades se establezcan en cualquier orden. Las propiedades deben ser
independientes con relación a otras propiedades. Suele ocurrir a menudo que la función de un
objeto no se activa hasta que el programador define un conjunto de propiedades específico; o hasta
que el objeto tiene un estado determinado. Si el objeto no tiene el estado correcto, la función no se
activa. Cuando el objeto tenga el estado correcto, la función se activará automáticamente sin
necesidad de una llamada explícita. La semántica es la misma independientemente del orden en el
que el programador establezca los valores de la propiedad, o de cómo consiga que el objeto tenga
el estado activo.
Por ejemplo, un control TextBox puede contener dos propiedades relacionadas: DataSource y
DataField. DataSource especifica el nombre de la tabla y DataField el nombre de la columna. Una
vez especificadas las dos propiedades, el control puede enlazar automáticamente datos de la tabla
a la propiedad Text del control. En el siguiente ejemplo de código se muestran las propiedades que
se pueden establecer en cualquier orden.
[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 propiedades DataSource y DataField se pueden establecer en cualquier orden. Por tanto, el
código anterior es equivalente a lo siguiente:
22

[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 puede establecer una propiedad como null (Nothing en Visual Basic) para indicar que
no 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 de código se muestra cómo realizar el seguimiento del estado de la función
de enlace de datos y activarla o desactivarla automáticamente en el momento adecuado.
[Visual Basic]
Public Class TextBox
Private m_dataSource As String
Private m_dataField As String
Private m_active As Boolean

Public Property DataSource() As String
23

Get
Return m_dataSource
End Get
Set
If 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 If
End Set
End Property
Public Property DataField() As String
Get
Return m_dataField
End Get
Set
If 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 If
End Set
End Property
Sub SetActive(m_value As Boolean)
If value <> m_active Then
If m_value Then
Activate()
Text = dataBase.Value(m_dataField)
Else
Deactivate()
Text = ""
End If
' Set active only if successful.
m_active = value
24

End If
End Sub
Sub Activate()
' Open database.
End Sub

Sub Deactivate()
' Close database.
End Sub
End 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
{
25

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. }
}
26

En el ejemplo anterior, la siguiente expresión determina si el objeto está en un estado en el que la
función de enlace de datos se activa automáticamente.
[Visual Basic]
(Not (value Is Nothing) And Not (m_dataField Is Nothing))
[C#]
value != null && dataField != null
La activación automática se define mediante la creación de un método que determina si el objeto se
puede activar en función de 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 propiedades relacionadas, como DataSource y DataMember, considere la posibilidad de
implementar ISupportInitialize (Interfaz). De este modo, el diseñador (o el usuario) podrá llamar a los
métodos ISupportInitialize.BeginInit e ISupportInitialize.EndInit al establecer varias propiedades para
que el componente pueda proceder a optimizar. En el ejemplo anterior, ISupportInitialize puede
evitar los intentos innecesarios de obtener acceso a la base de datos hasta que se haya realizado
correctamente la configuración.
La expresión que aparece en este método indica las partes del modelo de objetos que se deben
examinar para exigir estas transiciones de estado. En este caso, afecta a las propiedades
DataSource y DataField. Para obtener más información sobre la elección entre propiedades o
métodos, vea Propiedades frente a métodos.
Provocar eventos PropertyChanged
Los componentes deben provocar eventos PropertyChanged, si se desea notificar a los
consumidores cuando se cambia la propiedad del componente mediante programación. La
convención de nomenclatura para un evento PropertyChanged es agregar el sufijo Changed al
nombre de la propiedad, como en TextChanged. Por ejemplo, un control puede provocar un evento
TextChanged cuando se cambia la propiedad de texto. Para provocar este evento, se puede utilizar
la rutina auxiliar protegida Raise<Property>Changed. No obstante, es posible que la sobrecarga
para provocar el evento PropertyChanged y agregar un elemento a la tabla hash no compense. En
el siguiente ejemplo de código se muestra la implementación de una rutina auxiliar en un evento
PropertyChanged.
[Visual Basic]
Class Control
Inherits Component
Private m_text As String
Public Property Text() As String
27

Get
Return m_text
End Get
Set
If Not m_text.Equals(value) Then
m_text = value
RaiseTextChanged()
End If
End Set
End Property
End Class
[C#]
class Control: Component
{
string text;
public string Text
{
get
{ return text;
}
set
{
if (!text.Equals(value))
{ text = value;
RaiseTextChanged();
}
}
}
}
El enlace de datos utiliza este modelo para permitir el enlace de la propiedad en ambos sentidos.
Sin los eventos <Property>Changed y Raise<Property>Changed, el enlace de datos funciona en
un sentido; si la base de datos cambia, la propiedad se actualiza. Cada propiedad que provoca el
evento <Property>Changed debe proporcionar los metadatos que indican que la propiedad admite
el enlace de datos.
Conviene provocar eventos de cambio/cambiados, si el valor de una propiedad cambia debido a
operaciones externas. Estos eventos indican al programador que el valor de una propiedad está
cambiando o ha cambiado como resultado de una operación y no mediante una llamada al método
en el objeto.
28

La propiedad Text de un control Edit es un ejemplo claro. Mientras el usuario escribe información
en el control, el valor de la propiedad cambia automáticamente. Se provoca un evento antes de que
el valor de la propiedad cambie. No se pasa ni el valor antiguo ni el nuevo, y el programador puede
cancelar el evento iniciando una excepción. El nombre del evento es el nombre de la propiedad
seguido del sufijo Changing. En el siguiente ejemplo de código se muestra un evento de cambio.
[Visual Basic]
Class Edit
Inherits Control

Public Property Text() As String
Get
Return m_text
End Get
Set
If m_text <> value Then
OnTextChanging(Event.Empty)
m_text = value
End If
End Set
End Property
End Class
[C#]
class Edit : Control
{
public string Text
{
get
{ return text; }
set
{ if (text != value)
{ OnTextChanging(Event.Empty);
text = value;
}
}
}
}
29

También se provoca un evento después de cambiar el valor de la propiedad. Este evento no se
puede cancelar. El nombre del evento es el nombre de la propiedad seguido del sufijo Changed.
También se debe provocar el evento PropertyChanged genérico. El modelo para provocar estos
dos eventos es provocar el evento específico desde el método OnPropertyChanged. En el
siguiente ejemplo se muestra el uso del método OnPropertyChanged.
[Visual Basic]
Class Edit
Inherits Control
Public Property Text() As String
Get
Return m_text
End Get
Set
If m_text <> value Then
OnTextChanging(Event.Empty)
m_text = value
RaisePropertyChangedEvent(Edit.ClassInfo. m_text)
End If
End Set
End Property
Protected Sub OnPropertyChanged(e As PropertyChangedEventArgs)
If e.PropertyChanged.Equals(Edit.ClassInfo. m_text) Then
OnTextChanged(Event.Empty)
End If
If Not (onPropertyChangedHandler Is Nothing) Then
onPropertyChangedHandler(Me, e)
End If
End Sub
End Class
[C#]
class Edit : Control
{
public string Text
{ get
{ return text; }
set
{ if (text != value)
30

{ 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 de una propiedad no se almacena como campo, esto
complica el seguimiento de los cambios de valores. Al provocar el evento de cambio, busque todos
los lugares en los que el valor de la propiedad puede cambiar y proporcione la capacidad de
cancelar el evento. Por ejemplo, el control Edit del ejemplo anterior no es del todo exacto porque el
valor Text está almacenado en realidad en el identificador de ventana (HWND). Para provocar el
evento TextChanging, debe examinar los mensajes de Windows para determinar cuando puede
cambiar el texto; y permitir que se inicie una excepción en OnTextChanging para cancelar el
evento. Si es muy difícil proporcionar un evento de cambio, se puede admitir sólo el evento
cambiado.
Propiedades frente a métodos
Los diseñadores de bibliotecas de clases deben decidir a menudo entre implementar un miembro de
la clase como una propiedad o como un método. En general, los métodos representan acciones y
las propiedades representan datos. Utilice las instrucciones siguientes para decidir entre estas
opciones.
• Utilice una propiedad cuando el miembro sea un miembro de datos lógico. En las siguientes
declaraciones de miembro, Name es una propiedad porque es un miembro lógico de la clase.
[Visual Basic]
Public Property Name As String
Get
Return m_name
End Get
Set
m_name = value
31

End Set
End Property
[C#]
public string Name
get
{ 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, desea ponerse en contacto con el usuario
para que tenga en cuenta que debe almacenar el resultado en la caché.
• Obtiene el valor de una propiedad mediante el descriptor de acceso get y esta acción tiene
efectos secundarios visibles.
• Al llamar al miembro dos veces seguidas da lugar a resultados distintos.
• El orden de ejecución es importante. Tenga en cuenta que las propiedades de un tipo se
deben poder establecer y recuperar en cualquier orden.
• El miembro es estático, pero devuelve un valor que se puede cambiar.
• El miembro devuelve una matriz. Las propiedades que devuelven matrices pueden ser muy
imprecisas. Por lo general, es necesario devolver una copia de la matriz interna para que el
usuario no pueda cambiar el estado interno. Esto junto con el hecho de que el usuario
puede creer fácilmente que es una propiedad indizada, conduce a un código ineficaz. En el
siguiente ejemplo de código, cada llamada a la propiedad Methods crea una copia de la
matriz. Como resultado, se crearán 2
n
+1 copias de la matriz en el siguiente bucle.
[Visual Basic]
Dim type As Type = ' Get a type.
Dim i As Integer
For i = 0 To type.Methods.Length - 1
If type.Methods(i).Name.Equals("text") Then
' Perform some operation.
End If
Next i
[C#]
Type type = // Get a type.
for (int i = 0; i < type.Methods.Length; i++)
{
if (type.Methods[i].Name.Equals ("text"))
32

{ // Perform some operation. }
}
En el ejemplo siguiente se muestra el uso correcto de propiedades y métodos.
[Visual Basic]
Class Connection
' The following three members should be properties
' because they can be set in any order.
Property DNSName() As String
' Code for get and set accessors goes here.
End Property
Property UserName() As String
' Code for get and set accessors goes here.
End Property
Property Password() As String
'Code for get and set accessors goes here.
End Property
' The following member should be a method
' because the order 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 order.
string DNSName {get{};set{};}
string UserName {get{};set{};}
string Password {get{};set{};}

// The following member should be a method
// because the order of execution is important.
// This method cannot be executed until after the
// properties have been set.
bool Execute ();
}
33

Propiedades de sólo lectura y escritura
Se debe utilizar una propiedad de sólo lectura cuando el usuario no puede cambiar el miembro de
datos lógicos de la propiedad. No utilice propiedades de sólo escritura.
Uso de propiedades indizadas
Nota Las propiedades indizadas también se conocen como indizadores.
En las reglas siguientes se describen las instrucciones de utilización de propiedades indizadas:
• Utilice una propiedad indizada cuando el miembro de datos lógico de la propiedad sea una
matriz.
• Considere la utilización sólo de valores enteros o cadenas para las propiedades indizadas. Si el
diseño requiere otros tipos para las propiedades indizadas, reconsidere si representa un
miembro de datos lógico. Si no es así, use un método.
• Considere usar sólo un índice. Si el diseño requiere varios índices, reconsidere si representa un
miembro de 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 valor
predeterminado de esa clase. La compatibilidad con indizadores del lenguaje de programación
C# hace que se cumpla esta regla.
• No utilice propiedades indizadas no predeterminadas. 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 para los usuarios como la propiedad
Chars de 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 a
dos o más métodos sobrecargados. En el siguiente ejemplo de código, se debe cambiar la
propiedad Method al método GetMethod(string). Tenga en cuenta que esto no está permitido en
C#.
[Visual Basic]
' Change the MethodInfo Type.Method property to a method.
Property Type.Method(name As String) As MethodInfo
Function 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 MethodInfo
Function Type.GetMethod(name As String, ignoreCase As Boolean) As MethodInfo
[C#]
34

// 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)
35

Instrucciones de uso de eventos
En las reglas siguientes se describen las instrucciones de uso de eventos:
• Elija un nombre para el evento siguiendo las Instrucciones de nomenclatura de eventos
recomendadas.
• Cuando haga referencia a eventos en la documentación, utilice la frase, "se provocó un evento",
en vez de "se activó un evento" o "se desencadenó un evento".
• En los lenguajes que admiten la palabra clave void, utilice el tipo de valor devuelto de void para
los controladores de eventos, como se muestra en el siguiente código en lenguaje C# de
ejemplo.
public delegate void MouseEventHandler(object sender, MouseEventArgs e);
• Cuando un evento contenga datos importantes, tales como las coordenadas de un clic del
mouse (ratón), use clases de datos de eventos con establecimiento inflexible de tipos.
• Las clases de eventos deben extender la Clase System.EventArgs, como se muestra en el
ejemplo siguiente.
[Visual Basic]
Public Class MouseEventArgs
Inherits EventArgs
' Code for the class goes here.
End Class
[C#]
public class MouseEvent: EventArgs {}
• Utilice un método virtual protected (Protected en Visual Basic) para provocar cada evento.
Esta técnica no es adecuada para clases sealed porque no permiten derivar ninguna clase. La
finalidad de este método es proporcionar a una clase derivada una forma de controlar el evento
mediante un reemplazo. Esto es más natural que utilizar delegados en las situaciones en las
que el programador crea una clase derivada. El nombre del método se convierte en
OnEventName, donde EventName es el nombre del evento provocado. Por ejemplo:
[Visual Basic]
Public Class Button
Private onClickHandler As ButtonClickHandler
Protected Overridable Sub OnClick(e As ClickEventArgs)
' Call the delegate if non-null.
If Not (onClickHandler Is Nothing) Then
onClickHandler(Me, e)
End If
End Sub
End Class

36

[C#]
public class Button
{
ButtonClickHandler onClickHandler;
protected virtual void OnClick(ClickEventArgs e)
{ // Call the delegate if non-null.
if (onClickHandler != null)
onClickHandler(this, e);
}
}
La clase derivada puede elegir no llamar a la clase base durante el procesamiento de
OnEventName. Teniendo en cuenta esto, no incluya ningún procesamiento en el método
OnEventName que sea necesario para que la clase base funcione correctamente.
• Hay que tener en cuenta que un controlador de eventos puede contener cualquier código. Las
clases deben estar preparadas para que el controlador de eventos realice cualquier operación y,
en todos los casos, el objeto debe tener el estado adecuado, una vez provocado el evento.
Considere la utilización de un bloque try/finally en el punto del código donde se provoca el
evento. Como el programador puede realizar una función de devolución de llamada en el objeto
para realizar otras acciones, no haga suposiciones sobre el estado del objeto cuando el control
regrese al punto en el que provocó el evento. Por ejemplo:
[Visual Basic]
Public Class Button
Private onClickHandler As ButtonClickHandler
Protected Sub DoClick()
' Paint button in indented state.
PaintDown()
Try
' Call event handler.
OnClick()
Finally
' Window might be deleted in event handler.
If Not (windowHandle Is Nothing) Then
' Paint button in normal state.
PaintUp()
End If
End Try
End Sub
Protected Overridable Sub OnClick(e As ClickEvent)
37

If Not (onClickHandler Is Nothing) Then
onClickHandler(Me, e)
End If
End Sub
End Class
[C#]
public class Button
{ ButtonClickHandler onClickHandler;
protected void DoClick()
{ // Paint button in indented state.
PaintDown();
try
{ // Call event handler.
OnClick();
}
finally
{ // Window might be deleted 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.ComponentModel.CancelEventArgs para permitir que el
programador controle los eventos de un objeto. Por ejemplo, el control TreeView provoca
BeforeLabelEdit cuando el usuario se dispone a editar una etiqueta de nodo. En el siguiente
ejemplo de código se muestra cómo puede utilizar un programador este evento para evitar la
edición de un nodo.
[Visual Basic]
Public Class Form1
Inherits Form
Private treeView1 As New TreeView()
Sub treeView1_BeforeLabelEdit(source As Object, e As NodeLabelEditEventArgs)
38

e.CancelEdit = True
End Sub
End Class
[C#]
public class Form1: Form
{
TreeView treeView1 = new TreeView();
void treeView1_BeforeLabelEdit(object source,
NodeLabelEditEventArgs e)
{
e.CancelEdit = true;
}
}
Observe que en este caso, no se genera un error. Se trata de una etiqueta de sólo lectura.
Cancelar eventos no resulta adecuado en los casos en que el programador cancelaría la
operación y devolvería una excepción. En estos casos, se debe provocar una excepción dentro
del controlador de eventos para cancelarlos. Por ejemplo, supongamos que el usuario desea
escribir lógica de validación en un control de edición como se muestra a continuación.
[Visual Basic]
Public Class Form1
Inherits Form
Private edit1 As EditBox = New EditBox()
Sub TextChanging(source As Object, e As EventArgs)
Throw New RuntimeException("Invalid edit")
End Sub
End Class
[C#]
public class Form1: Form
{ EditBox edit1 = new EditBox();
void TextChanging(object source, EventArgs e)
{ throw new RuntimeException("Invalid edit"); }
39

Instrucciones de uso de métodos
En las reglas siguientes se describen las instrucciones de uso de métodos:
• Elija un nombre para el evento siguiendo las Instrucciones de nomenclatura de eventos.
• No utilice la notación húngara.
• De forma predeterminada, los métodos no son virtuales. Mantenga esta característica
predeterminada en las situaciones en las que no es necesario utilizar métodos virtuales. Para
obtener más información sobre la herencia de implementación, vea Instrucciones de uso de
clases base.
Instrucciones de sobrecarga de métodos
La sobrecarga de métodos se produce cuando una clase contiene dos métodos con el mismo
nombre, pero firmas diferentes. En esta sección se proporcionan algunas instrucciones de
utilización de métodos sobrecargados.
• Utilice la sobrecarga de métodos para proporcionar métodos diferentes que semánticamente
hagan lo mismo.
• Utilice la sobrecarga de métodos, en vez de permitir argumentos predeterminados. Los
argumentos predeterminados no controlan bien las versiones y, por tanto, no se permiten en
Common Language Specification (CLS). En el siguiente ejemplo de código se muestra un
método String.IndexOf sobrecargado.
[Visual Basic]
Function String.IndexOf(name As String) As Integer
Function String.IndexOf(name As String, startIndex As Integer) As Integer
[C#]
int String.IndexOf (String name);
int String.IndexOf (String name, int startIndex);
• Utilice los valores predeterminados correctamente. En una familia de métodos sobrecargados,
el método complejo debe utilizar nombres de parámetros que indiquen un cambio del estado
predeterminado que se supone en el método sencillo. Por ejemplo, en el código siguiente el
primer método supone que la búsqueda no hará distinción entre mayúsculas y minúsculas. En
el segundo método se utiliza el nombre ignoreCase en vez de caseSensitive para indicar cómo
se cambia el comportamiento predeterminado.
[Visual Basic]
' Method #1: ignoreCase = false.
Function Type.GetMethod(name As String) As MethodInfo
' Method #2: Indicates how the default behavior of method #1
' is being changed.
Function Type.GetMethod(name As String, ignoreCase As Boolean) As MethodInfo
[C#]
// Method #1: ignoreCase = false.
40

MethodInfo Type.GetMethod(String name);
// Method #2: Indicates how the default behavior of method #1 is being // changed.
MethodInfo Type.GetMethod (String name, Boolean ignoreCase);
• Utilice un modelo de nomenclatura y de orden coherentes en los parámetros del método. Lo
habitual es proporcionar un conjunto de métodos sobrecargados con un número creciente de
parámetros para que el programador pueda especificar el nivel de información deseado.
Cuantos más parámetros especifique, más detalles puede especificar el programador. En el
siguiente ejemplo de código, el método Execute contiene un orden de parámetros coherente y
una variación del modelo de nomenclatura. Cada variación del método Execute utiliza la misma
semántica para el conjunto compartido de parámetros.
[Visual Basic]
Public Class SampleClass
Private defaultForA As String = "default value for a"
Private defaultForB As Integer = "42"
Private defaultForC As Double = "68.90"

Overloads Public Sub Execute()
Execute(defaultForA, defaultForB, defaultForC)
End Sub

Overloads Public Sub Execute(a As String)
Execute(a, defaultForB, defaultForC)
End Sub

Overloads Public Sub Execute(a As String, b As Integer)
Execute(a, b, defaultForC)
End Sub

Overloads Public Sub Execute(a As String, b As Integer, c As Double)
Console.WriteLine(a)
Console.WriteLine(b)
Console.WriteLine(c)
Console.WriteLine()
End Sub
End Class
[C#]
public class SampleClass
41

{
readonly string defaultForA = "default value for a";
readonly int defaultForB = "42";
readonly double defaultForC = "68.90";

public void Execute()
{ Execute(defaultForA, defaultForB, defaultForC); }

public void Execute (string a)
{ Execute(a, defaultForB, defaultForC); }

public void Execute (string a, int b)
{ Execute (a, b, defaultForC); }

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 del grupo que debe ser virtual es el que más parámetros
tiene y sólo cuando se precisa extensibilidad.
• Si debe proporcionar la capacidad de reemplazar un método, establezca sólo la sobrecarga
más completa como virtual y defina las otras operaciones en función de esta sobrecarga. En el
ejemplo siguiente se muestra este modelo.
[Visual Basic]
Public Class SampleClass
Private myString As String
Public Sub New(str As String)
Me.myString = str
End Sub
Overloads Public Function IndexOf(s As String) As Integer
Return IndexOf(s, 0)
End Function
Overloads Public Function IndexOf(s As String, startIndex As
42

Integer) As Integer
Return IndexOf(s, startIndex, myString.Length - startIndex)
End Function
Overloads Public Overridable Function IndexOf(s As String,
startIndex As Integer, count As Integer) As Integer
Return myString.IndexOf(s, startIndex, count)
End Function
End Class
[C#]
public class SampleClass
{
private string myString;
public MyClass(string str)
{ this.myString = str; }

public int IndexOf(string s)
{ return IndexOf (s, 0); }

public int IndexOf(string s, int startIndex)
{return IndexOf(s, startIndex, myString.Length - startIndex ); }

public virtual int IndexOf(string s, int startIndex, int count)
{return myString.IndexOf(s, startIndex, count); }
}
Métodos con un número variable de argumentos
Puede que desee exponer un método que incluya un número variable de argumentos. Un ejemplo
clásico es el método printf del lenguaje de programación C. En las bibliotecas de clases
administradas, utilice la palabra clave params (ParamArray en Visual Basic) para esta
construcción. Por ejemplo, utilice el siguiente código en vez de varios métodos sobrecargados.
[Visual Basic]
Sub Format(formatString As String, ParamArray args() As Object)
[C#]
void Format(string formatString, params object [] args)
No es conveniente utilizar únicamente la convención de llamada VarArgs o puntos suspensivos (...)
ya que no es compatible con Common Language Specification.
En un código sensible al rendimiento, puede proporcionar rutas de acceso de código especiales
para un reducido número de elementos. Sólo se debe hacer esto para la ruta de acceso a un código
43

completo de un caso especial (no únicamente crear una matriz y llamar al método general). En
estos casos, se recomienda utilizar el siguiente modelo para que haya un equilibrio entre el
rendimiento y el costo del código de 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, params object [] args)
44

Instrucciones de uso de constructores
En las reglas siguientes se describen las instrucciones de uso de constructores:
• Proporcione un constructor privado predeterminado cuando sólo haya propiedades y métodos
estáticos en una clase. En el siguiente ejemplo, el constructor privado evita que se genere una
clase.
[Visual Basic]
NotInheritable Public Class Environment
' Private constructor prevents the class from being created.
Private Sub New()
' Code for the constructor goes here.
End Sub
End Class
[C#]
public sealed class Environment
{ // Private constructor prevents the class from being created.
private Environment()
{ // Code for the constructor goes here. }
}
• Reduzca al mínimo la cantidad de trabajo realizado en el constructor. Los constructores no
deberían hacer más que capturar el parámetro o parámetros de constructor. Esto retrasa el
costo que suponen más operaciones hasta que el usuario utilice una función específica de la
instancia.
• Proporcione un constructor para cada clase. Si no se puede crear un tipo, use un constructor
privado. Si no especifica un constructor, muchos lenguajes de programación (como C#) agregan
implícitamente un constructor público predeterminado. Si la clase es abstracta, agrega un
constructor protegido.
Tenga en cuenta que si agrega un constructor no predeterminado a una clase en una
compilación posterior de la versión, el constructor predeterminado implícito será eliminado, lo
que puede dañar el código de cliente. Por lo tanto, lo mejor es especificar siempre de forma
explícita el constructor, aunque se trate de un constructor público predeterminado.
• Proporcione un constructor protected (Protected en Visual Basic) que se pueda utilizar en los
tipos de una clase derivada.
• No proporcione constructores sin parámetros para los valores del tipo struct. Tenga en cuenta
que muchos compiladores no admiten que struct tenga un constructor sin parámetros. Si no se
incluye un constructor, el tiempo de ejecución inicializa todos los campos de struct con un valor
cero. Esto hace que la creación de campos estáticos y matrices sea más rápida.
• Utilice los parámetros de los constructores como un método abreviado para establecer
propiedades. No debe haber ninguna diferencia semántica entre un constructor vacío seguido
45

de descriptores de acceso set y un constructor con múltiples argumentos. Los tres ejemplos de
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 modelo de nomenclatura y de orden coherente en los parámetros de constructores.
Un modelo común de parámetros de constructores sirve para proporcionar un número de
parámetros cada vez mayor que permita a los programadores especificar el nivel de información
deseado. Cuantos más parámetros especifique, más detalles puede especificar el programador.
En el siguiente código de ejemplo, se muestra un modelo de orden y nomenclatura de
parámetros coherente para todos los constructores de SampleClass.
[Visual Basic]
Public Class SampleClass
Private Const defaultForA As String = "default value for a"
Private Const defaultForB As String = "default value for b"
Private Const defaultForC As String = "default value for c"
Public Sub New()
MyClass.New(defaultForA, defaultForB, defaultForC)
Console.WriteLine("New()")
End Sub
46

Public Sub New(a As String)
MyClass.New(a, defaultForB, defaultForC)
End Sub
Public Sub New(a As String, b As String)
MyClass.New(a, b, defaultForC)
End Sub
Public Sub New(a As String, b As String, c As String)
Me.a = a
Me.b = b
Me.c = c
End Sub
End Class
[C#]
public class SampleClass
{
private const string defaultForA = "default value for a";
private const string defaultForB = "default value for b";
private const string defaultForC = "default value for c";
public MyClass():this(defaultForA, defaultForB, defaultForC) {}
public MyClass (string a) : this(a, defaultForB, defaultForC) {}
public MyClass (string a, string b) : this(a, b, defaultForC) {}
public MyClass (string a, string b, string c)
}
Instrucciones de uso de campos
En las reglas siguientes se describen las instrucciones de uso de campos:
• No utilice campos de instancias que sean public o protected (Public o Protected en Visual
Basic). Si evita exponer directamente los campos al programador, se pueden tener versiones de
las clases más fácilmente ya que no se puede cambiar un campo en una propiedad y seguir
manteniendo la compatibilidad binaria. Considere la inclusión de los descriptores de acceso de
las propiedades get y set en los campos, en vez de hacerlos públicos. La presencia de código
ejecutable en los descriptores de acceso de las propiedades get y set, permite introducir
mejoras más adelante, como crear un objeto a petición, cuando se utiliza la propiedad o en la
notificación de cambio de propiedad. En el siguiente ejemplo de código se muestra el uso
correcto de campos de instancia privados con los descriptores de acceso de las propiedades
get y set.
[Visual Basic]
Public Structure Point
47

Private xValue As Integer
Private yValue As Integer
Public Sub New(x As Integer, y As Integer)
Me.xValue = x
Me.yValue = y
End Sub
Public Property X() As Integer
Get
Return xValue
End Get
Set
xValue = value
End Set
End Property
Public Property Y() As Integer
Get
Return yValue
End Get
Set
yValue = value
End Set
End Property
End 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
48

{ return xValue; }
set
{ xValue = value; }
}
public int Y
{
get
{ return yValue; }
set
{ yValue = value; }
}
}
• Exponga un campo a una clase derivada utilizando una propiedad protected que devuelve el
valor del campo. Esto se muestra en el siguiente ejemplo de código:
[Visual Basic]
Public Class Control
Inherits Component
Private handle As Integer
Protected ReadOnly Property Handle() As Integer
Get
Return handle
End Get
End Property
End Class
[C#]
public class Control: Component
{
private int handle;
protected int Handle
{ get
{ return handle; }
}
}
• Use la palabra clave const (Const en Visual Basic) para declarar campos de constantes que no
vayan a cambiar. Los compiladores de lenguajes guardan los valores de los campos const
directamente en código de llamada.
49

• Utilice campos estáticos públicos de sólo lectura para instancias de objetos predefinidas. Si ya
hay instancias predefinidas de un objeto, declárelas como campos estáticos públicos de sólo
lectura del objeto. Utilice el estilo de Mayúsculas y minúsculas Pascal pues son campos
públicos. En el siguiente ejemplo de código se muestra el uso correcto de campos estáticos
públicos de sólo lectura.
[Visual Basic]
Public Structure Color
Public 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 code here.
End Sub
Public Sub New(r As Byte, g As Byte, b As Byte)
' Insert code here.
End Sub
Public ReadOnly Property RedValue() As Byte
Get
Return Color
End Get
End Property
Public ReadOnly Property GreenValue() As Byte
Get
Return Color
End Get
End Property
Public ReadOnly Property BlueValue() As Byte
Get
Return Color
End Get
End Property
End Structure
[C#]
public struct Color
{ public static readonly Color Red = new Color(0x0000FF);
50

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 code here.}
public Color(byte r, byte g, byte b)
{ // Insert code 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 de campo. Utilice abreviaturas
sólo si los programadores en general las comprenden. No utilice mayúsculas en los nombres de
campos. A continuación, se muestra un ejemplo de nombres de campos correctos.
[Visual Basic]
Class SampleClass
Private url As String
Private destinationUrl As String
End Class
[C#]
class SampleClass
{ string url;
string destinationUrl; }
• No utilice la notación húngara en nombres de campos. Los nombres correctos describen la
semántica y no el tipo.
• No aplique un prefijo a nombres de campos o a nombres de campos estáticos. En concreto, no
aplique un prefijo al nombre de un campo para diferenciar los campos estáticos de los campos
no estáticos. Por ejemplo, aplicar un prefijo g_ o s_ es incorrecto.
Instrucciones de uso de parámetros
En las reglas siguientes se describen las pautas de uso de parámetros:
• Compruebe si los argumentos de los parámetros son válidos. Realice la validación de
argumentos para cada método protegido o público y descriptor de accesos de la propiedad set.
Inicie excepciones significativas para el programador en los argumentos de parámetros no
51

válidos. Use la clase System.ArgumentException o una clase derivada de
System.ArgumentException. En el siguiente ejemplo se comprueba si los argumentos de los
parámetros son válidos y se inician excepciones significativas.
[Visual Basic]
Class SampleClass
Private countValue As Integer
Private maxValue As Integer = 100

Public Property Count() As Integer
Get
Return countValue
End Get
Set
' Check for valid parameter.
If value < 0 Or value >= maxValue Then
Throw New ArgumentOutOfRangeException("value", value,
"Value is invalid.")
End If
countValue = value
End Set
End Property
Public Sub SelectItem(start As Integer, [end] As Integer)
' Check for valid parameter.
If start < 0 Then
Throw New ArgumentOutOfRangeException("start", start, "Start
is invalid.")
End If
' Check for valid parameter.
If [end] < start Then
Throw New ArgumentOutOfRangeException("end", [end], "End is
invalid.")
End If
' Insert code to do other work here.
Console.WriteLine("Starting at {0}", start)
Console.WriteLine("Ending at {0}", [end])
End Sub
52

End Class
[C#]
class SampleClass
{
public int Count
{ get
{ return count; }
set
{ // Check for valid parameter.
if (count < 0 || count >= MaxValue)
throw newArgumentOutOfRangeException(
Sys.GetString(
"InvalidArgument","value",count.ToString()));
}
}
public void Select(int start, int end)
{
// Check for valid parameter.
if (start < 0)
throw new ArgumentException(
Sys.GetString("InvalidArgument","start",start.ToString()));
// Check for valid parameter.
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 los
métodos public o protected. Se puede producir en un nivel inferior de rutinas privadas. La
cuestión principal es que en todo el área de la superficie que se expone al programador se
comprueba si los argumentos son válidos.
• Asegúrese de comprender totalmente las implicaciones de pasar parámetros por valor o por
referencia. Pasar un parámetro por valor copia el valor que se está pasando y no produce
ningún efecto en el valor original. En el siguiente ejemplo de método se pasan parámetros por
valor.
public void Add(object value){}
53

Pasar un parámetro por referencia pasa la ubicación de almacenamiento del valor. Como
resultado, se pueden realizar cambios en el valor del parámetro. En el siguiente ejemplo de
método se pasa un parámetro por referencia.
public static int Exchange(ref int location, int value){}
Un parámetro de salida representa la misma ubicación de almacenamiento que la variable
especificada como argumento en la invocación del método. Como resultado, sólo se pueden
realizar cambios en el parámetro de salida. En el siguiente ejemplo de método se pasa un
parámetro de salida.
[DllImport("Kernel32.dll"]
public static extern bool QueryPerformanceCounter(out long value)
54

Instrucciones de uso de tipos
Los tipos constituyen las unidades de encapsulación en Common Language Runtime. Para obtener
una descripción detallada de la lista completa de tipos de datos compatibles con el motor de tiempo
de ejecución, vea Sistema de tipos común. En esta sección se proporcionan algunas instrucciones
de utilización de clases básicas de tipos.
Instrucciones de uso de clases base
Una clase es uno de los tipos más común. Las clases pueden ser abstractas o selladas. Una clase
abstracta requiere una clase derivada para proporcionar una implementación. Una clase sealed no
admite una clase derivada. Es conveniente utilizar clases, en vez de otros tipos.
Las clases base son una forma útil de agrupar objetos que comparten un conjunto común de
funcionalidades. Las clases base pueden proporcionar un conjunto predeterminado de
funcionalidades a la vez que permiten la personalización mediante la ampliación.
Se debería proporcionar un constructor de forma explícita para todas las clases, ya que,
normalmente, los compiladores agregan un constructor público predeterminado a las clases que no
definen un constructor. Si la intención es que la clase no se pueda crear, el hecho de que contenga
dicho constructor público predeterminado puede resultar confuso para los usuarios de ésta. Por lo
tanto, lo mejor es definir siempre al menos un constructor por clase. Si no se desea que ésta se
pueda crear, se puede configurar el constructor como privado.
Debe agregar extensibilidad o polimorfismo al diseño sólo si tiene un escenario del cliente claro. Por
ejemplo, proporcionar una interfaz para adaptadores de datos es complicado y no ofrece beneficios
reales. Además, los programadores deberán programar específicamente cada adaptador, por tanto,
el beneficio que proporciona una interfaz es mínimo. Sin embargo, no es necesario que sean
coherentes entre todos los adaptadores. Aunque una interfaz o clase abstracta no es lo apropiado
en esta situación, es muy importante proporcionar un modelo coherente. Se pueden proporcionar
modelos coherentes para programadores en las clases base. Siga estas instrucciones para crear
clases base.
Clases base e interfaces
Un tipo de interfaz es una especificación de un protocolo, potencialmente compatible con muchos
tipos de objetos. Utilice clases base en vez de interfaces siempre que sea posible. Desde la
perspectiva de las versiones, las clases son más flexibles que las interfaces. Con una clase, se
puede 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 derivadas existentes seguirán funcionando sin
sufrir ningún cambio.
Como las interfaces no son compatibles con la herencia de implementación, el modelo aplicado a
las clases no se aplica a las interfaces. Agregar un método a una interfaz es lo mismo que agregar
un método abstracto a una clase base; cualquier clase que implemente la interfaz interrumpirá la
ejecución porque la clase no implementa el nuevo método.
La utilización de interfaces es adecuada 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 de interfaz de
usuario (UI) y otras son servicios Web XML).
55

• La agregación no es adecuada o no es viable.
En el resto de las situaciones, es mejor utilizar el modelo de herencia de clases.
Constructores y métodos protegidos
Proporcione la personalización de clases a través de métodos protegidos. La interfaz pública de una
clase base debe proporcionar un conjunto completo de funcionalidades para el consumidor de la
clase. Sin embargo, los usuarios de la clase a menudo desean implementar el menor número de
métodos posible y, de este modo, proporcionar el conjunto completo de funcionalidades al
consumidor. Con esta finalidad, proporcione un conjunto de métodos públicos finales o no virtuales
que llaman a través de un único método protegido que proporciona implementaciones para los
métodos. Este método se debe marcar con el sufijo Impl. La utilización de este modelo también
proporciona lo que se conoce como método Template. En el siguiente ejemplo de código se
muestra este proceso.
[Visual Basic]
Public Class SampleClass
Private x As Integer
Private y As Integer
Private width As Integer
Private height As Integer
Private specified As BoundsSpecified
Overloads Public Sub SetBounds(x As Integer, y As Integer, width As
Integer, height As Integer)
SetBoundsImpl(x, y, width, height, Me.specified)
End Sub
Overloads Public Sub SetBounds(x As Integer, y As Integer, width As
Integer, height As Integer, specified As BoundsSpecified)
SetBoundsImpl(x, y, width, height, specified)
End Sub
Protected Overridable Sub SetBoundsImpl(x As Integer, y As Integer,
width As Integer, height As Integer,
specified As BoundsSpecified)
' Insert code to perform meaningful operations here.
Me.x = x
Me.y = y
Me.width = width
Me.height = height
Me.specified = specified
Console.WriteLine("x {0}, y {1}, width {2}, height {3}, bounds {4}",
Me.x, Me.y, Me.width, Me.height, Me.specified)
56

End Sub
End 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, int
height, BoundsSpecified specified)
{ // Add code 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 de los compiladores, como el de C#,
insertarán uno. Por tanto, para una mejor documentación y legibilidad del código fuente, deberá
definir explícitamente un constructor protected en todas las clases abstractas.
Instrucciones de uso de clases sealed
Utilice clases sealed si sólo hay propiedades y métodos estáticos en una clase.
Instrucciones de uso de tipos de valor
Un tipo de valor describe un valor que se representa como una secuencia de bits almacenada en la
pila. Para obtener una descripción de todos los tipos de datos integrados de .NET Framework, vea
57

Tipos de valor. En esta sección se proporcionan las instrucciones de uso de tipos de valor de
enumeraciones (enum) y de estructuras (struct).
Instrucciones de uso de estructuras
Se recomienda utilizar un valor struct para los tipos que cumplan los siguientes criterios:
• Actúan como tipos primitivos.
• Tienen un tamaño de instancia inferior a 16 bytes.
• Son inmutables.
• Es conveniente utilizar la semántica de valor.
En el siguiente ejemplo se muestra una estructura definida correctamente.
[Visual Basic]
Public Structure Int32
Implements IFormattable
Implements IComparable
Public Const MinValue As Integer = -2147483648
Public Const MaxValue As Integer = 214748364
Private intValue As Intege
Overloads Public Shared Function ToString(i As Integer) As String
' Insert code here.
End Function
Overloads Public Function ToString(ByVal format As String, ByVal
formatProvider As IFormatProvider) As String Implements
IFormattable.ToString
' Insert code here.
End Function
Overloads Public Overrides Function ToString() As String
' Insert code here.
End Function
Public Shared Function Parse(s As String) As Integer
' Insert code here.
Return 0
End Function
Public Overrides Function GetHashCode() As Integer
' Insert code here.
Return 0
End Function
58

Public Overrides Overloads Function Equals(obj As Object) As Boolean
' Insert code here.
Return False
End Function
Public Function CompareTo(obj As Object) As Integer Implements
IComparable.CompareTo
' Insert code here.
Return 0
End Function
End Structure
[C#]
public struct Int32: IComparable, IFormattable
{
public const int MinValue = -2147483648;
public const int MaxValue = 2147483647;
public static string ToString(int i)
{ // Insert code here. }

public string ToString(string format, IFormatProvider formatProvider)
{ // Insert code here. }

public override string ToString()
{ // Insert code here. }

public static int Parse(string s)
{ // Insert code here.
return 0; }

public override int GetHashCode()
{ // Insert code here.
return 0; }

public override bool Equals(object obj)
{ // Insert code here.
return false; }

59

public int CompareTo(object obj)
{ // Insert code here.
return 0; }
}
• No incluya un constructor predeterminado para struct. Tenga en cuenta que C# no admite que
struct tenga un constructor predeterminado. El motor en tiempo de ejecución inserta un
constructor que inicializa todos los valores en un estado cero. Esto permite crear matrices de
estructuras sin ejecutar el constructor en cada instancia. No haga que una estructura struct
dependa de un constructor que se invoque para cada instancia. Las instancias de estructuras se
pueden crear con un valor de cero sin ejecutar un constructor. También se debe diseñar una
estructura struct para un estado en el que todos los datos de instancias se establezcan en
cero, false o null (según corresponda) para que sean válidos.
Instrucciones de uso de enumeraciones
En las reglas siguientes se describen las pautas de uso de enumeraciones:
• No utilice el sufijo Enum en tipos enum.
• Utilice un tipo de valor enum para escribir tipos devueltos, parámetros, propiedades seguros.
Defina siempre los valores enumerados que se utilizan en parámetros y propiedades mediante
el tipo enum. De este modo, las herramientas de desarrollo conocen los valores posibles de
una propiedad o parámetro. En el ejemplo siguiente se muestra cómo definir un tipo enum.
[Visual Basic]
Public Enum FileMode
Append
Create
CreateNew
Open
OpenOrCreate
Truncate
End Enum
60

[C#]
public enum FileMode
{ Append,
Create,
CreateNew,
Open,
OpenOrCreate,
Truncate
}
En el ejemplo siguiente se muestra el constructor de un objeto FileStream que utiliza la
enumeración FileMode.
[Visual Basic]
Public Sub New(ByVal path As String, ByVal mode As FileMode);
[C#]
public FileStream(string path, FileMode mode);
• Utilice un tipo de valor enum en vez de constantes estáticas finales.
• No utilice un tipo de valor enum en conjuntos abiertos (como la versión del sistema operativo).
• Utilice la Clase System.FlagsAttribute para crear atributos personalizados para un tipo de valor
enum sólo si se realiza una operación OR bit a bit en valores numéricos. Use potencias de dos
para los valores de enum de forma que puedan combinarse con facilidad. Este atributo se
aplica en el siguiente ejemplo de código.
[Visual Basic]
<Flags()>
Public Enum WatcherChangeTypes
Created = 1
Deleted = 2
Changed = 4
Renamed = 8
All = Created Or Deleted Or Changed Or Renamed
End Enum
[C#]
[Flags()]
public enum WatcherChangeTypes
{ Created = 1,
Deleted = 2,
Changed = 4,
Renamed = 8,
61

All = Created | Deleted | Changed | Renamed
};
Nota Una excepción a esta regla es la encapsulación de una API de Win32. Normalmente,
se utilizan definiciones internas que vienen en un encabezado Win32. Se pueden dejar
estas definiciones con las mayúsculas y minúsculas de Win32 que, por lo general, están
todas las letras en mayúsculas.
• Considere proporcionar constantes con nombre para las combinaciones de indicadores usadas
con frecuencia. Usar una operación OR bit a bit es un concepto avanzado y no debería
requerirse para tareas sencillas. Esto se muestra en el siguiente ejemplo de enumeración:
[Visual Basic]
<Flags()> _
Public Enum FileAccess
Read = 1
Write = 2
ReadWrite = Read Or Write
End Enum
[C#]
[Flags()]
public enum FileAccess
{
Read = 1,
Write = 2,
ReadWrite = Read | Write,
}
• Utilice el tipo Int32 como el tipo subyacente de un tipo de valor enum salvo que se cumpla una
de las siguiente condiciones:
• El tipo de valor enum representa indicadores y hay ya más de 32 indicadores; o el tipo de
valor enum podría tener muchos indicadores en el futuro.
• El tipo debe ser diferente de int para que sea compatible con versiones anteriores.
• Tenga en cuenta que los argumentos enum no siempre se encuentran en el intervalo definido.
Resulta válido convertir cualquier valor entero al tipo enum aunque el valor no esté definido en
el tipo enum. Realice una validación de argumentos cómo se muestra en el siguiente ejemplo
de código.
[Visual Basic]
Public Sub SetColor(newColor As Color)
If Not [Enum].IsDefined(GetType(Color), newColor) Then
Throw New ArgumentOutOfRangeException()
End If
62

End Sub
[C#]
public void SetColor (Color color)
{ if (!Enum.IsDefined (typeof(Color), color)
throw new ArgumentOutOfRangeException();
}
Instrucciones de uso de delegados
Los delegados son una herramienta eficaz que permite al diseñador de modelos de objetos de
código administrado encapsular llamadas a métodos. Los delegados son muy útiles en las
notificaciones de eventos y en las funciones de devolución de llamada.
Notificaciones de eventos
Utilice el modelo de diseño de eventos adecuado aunque el evento no esté relacionado con la
interfaz de usuario. Para obtener más información sobre la utilización de eventos, vea Instrucciones
de uso de eventos.
Funciones de devolución de llamada
Las funciones de devolución de llamada se pasan a un método para que se pueda llamar al código
de usuario varias veces durante la ejecución para proporcionar personalización. Pasar una función
de devolución de llamada Compare a una rutina de ordenación es un ejemplo clásico de utilización
de estas funciones. Estos métodos deben utilizar las convenciones de las funciones de devolución
de llamada descritas en la sección Uso de las funciones de devolución de llamada.
Termine los nombres de funciones de devolución de llamada con el sufijo Callback.
Instrucciones de uso de atributos
.NET Framework permite a los programadores inventar nuevas clases de información declarativa,
especificar información declarativa para entidades de programas diversos y recuperar la información
sobre atributos en el entorno del motor de tiempo de ejecución. Por ejemplo, en un marco de trabajo
se puede definir un atributo HelpAttribute, que se puede colocar en elementos de programa como
clases y métodos, para proporcionar una asignación de los elementos del programa a la
documentación correspondiente. En la declaración de clases de atributos se pueden definir nuevas
clases de información declarativa, que pueden tener parámetros con nombres y parámetros
posicionales. Para obtener más información, vea Escribir atributos personalizados.
En las reglas siguientes se describen las instrucciones de uso de las clases de atributos:
• Agregue el sufijo Attribute a clases de atributos personalizados, como se muestra en el
siguiente ejemplo.
[Visual Basic]
Public Class ObsoleteAttribute{}
[C#]
public class ObsoleteAttribute{}
63

• Especifique AttributeUsage en los atributos para definir su utilización exacta, como se muestra
en el siguiente ejemplo.
[Visual Basic]
<AttributeUsage(AttributeTargets.All, Inherited := False, AllowMultiple := True)> _
Public Class ObsoleteAttribute
Inherits Attribute
' Insert code here.
End Class
[C#]
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
public class ObsoleteAttribute: Attribute {}
• Selle las clases de atributos siempre que sea posible, para que no se pueda derivar ninguna
clase.
• Utilice argumentos posicionales (parámetros del constructor) en los parámetros requeridos.
Proporcione una propiedad de sólo lectura con el mismo nombre que el argumento posicional,
pero cambie el uso de las mayúsculas y minúsculas para diferenciarlos. Esto permite el acceso
al argumento en tiempo de ejecución.
• Utilice argumentos con nombres (propiedades de lectura y escritura) en los parámetros
opcionales. Proporcione una propiedad de lectura/escritura con el mismo nombre que el
argumento con nombre, pero cambie el uso de las mayúsculas y minúsculas para diferenciarlos.
• No defina un parámetro con argumentos posicionales y argumentos con nombre. En el ejemplo
siguiente se muestra este modelo.
[Visual Basic]
Public Class NameAttribute
Inherits Attribute
' This is a positional argument.
Public Sub New(username As String)
' Implement code here.
End Sub
Public ReadOnly Property UserName() As String
Get
Return UserName
End Get
End Property
' This is a named argument.
Public Property Age() As Integer
Get
Return Age
64

End Get
Set
Age = value
End Set
End Property
End Class
[C#]
public class NameAttribute: Attribute
{ // This is a positional argument.
public NameAttribute (string username)
{ // Implement code here. }
public string UserName
{ get
{ return UserName; }
}
// This is a named argument.
public int Age
{ get
{ return Age; }
set
{ Age = value; }
}
}
Instrucciones de uso de tipos anidados
Los tipos anidados son tipos definidos dentro del ámbito de otro tipo. Los tipos anidados resultan
muy útiles para encapsular los detalles de implementación de un tipo, como un enumerador en una
colección, ya que pueden tener acceso al estado privado.
Los tipos anidados públicos se deben utilizar en contadas ocasiones. Utilícelos sólo en situaciones
en las que se cumplan las condiciones siguientes:
• El tipo anidado (tipo interno) pertenece de forma lógica al tipo contenedor (tipo externo).
En los ejemplos siguientes se muestra cómo definir tipos con y sin tipos anidados:
[Visual Basic]
' With nested types.
ListBox.SelectedObjectCollection
' Without nested types.
ListBoxSelectedObjectCollection
65

' 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.
RichTextBoxScrollBars
No utilice tipos anidados si se cumplen las siguientes condiciones:
• Las instancias del tipo deben crearse mediante el código de cliente. Si un tipo tiene un
constructor público, probablemente no debería anidarse. La razón tras esta directriz es que si se
pueden crear instancias de un tipo anidado, esto indicaría que el tipo ocupa un lugar en su
propia biblioteca. Se puede crear, usar y destruir sin usar el tipo externo; por lo tanto, no
debería estar anidado. Un tipo interno no se debería reutilizar ampliamente fuera del tipo
externo sin una relación con el tipo externo.
• Las referencias al tipo se declaran normalmente en el código de cliente.
Instrucciones para exponer funcionalidad a COM
Common Language Runtime proporciona compatibilidad total para interoperar con componentes
COM. Un componente COM se puede utilizar dentro de un tipo administrado y una instancia
administrada se puede utilizar en un componente COM. Esta compatibilidad es la clave para
desplazar una parte del código no administrado al código administrado cada vez; sin embargo,
también presenta algunos problemas para los diseñadores de bibliotecas de clases. Para exponer
totalmente un tipo administrado a clientes COM, el tipo debe exponer funcionalidad de forma que
sea compatible y respete los términos del contrato de control de versiones de COM.
Marque las bibliotecas de clases administradas con el atributo ComVisibleAttribute para indicar si
los clientes COM pueden utilizar la biblioteca directamente o deben utilizar un contenedor para
adaptar las funcionalidades.
Los tipos y las interfaces que se deben utilizar directamente en los clientes COM, como alojarlos en
un contenedor no administrado, se deben marcar con el atributo ComVisible(true). El cierre
transitivo de todos los tipos a los que se hace referencia en los tipos expuestos debe marcarse
explícitamente como ComVisible(true); en caso contrario, se expondrán como IUnknown.
Nota Los miembros de un tipo también deben marcarse como ComVisible(false); esto reduce
la exposición a COM y, por tanto, las restricciones en la utilización de tipos administrados.
66

Los tipos marcados con el atributo ComVisible(true) no pueden exponer las funcionalidades
únicamente de forma que no se puedan utilizar en COM. En concreto, COM no es compatible con
los métodos estáticos ni con los constructores parametrizados. Pruebe la funcionalidad del tipo en
clientes COM para comprobar que su comportamiento es correcto. Asegúrese de que comprende el
impacto que tiene en el Registro hacer que todos los tipos se puedan crear conjuntamente.
Cálculo por referencia
Los objetos de cálculo por referencia son Objetos utilizables de forma remota. La interacción remota
de objetos se aplica a las tres clases de tipos siguientes:
• Tipos cuyas instancias se copian cuando se calculan sus referencias a través de un límite
AppDomain (en el mismo equipo o en un equipo diferente). Estos tipos se deben marcar con el
atributo Serializable.
• Tipos para los que el motor de ejecución crea un proxy transparente cuando se calculan sus
referencias a través de un límite AppDomain (en el mismo equipo o en un equipo diferente).
Estos tipos se deben derivar en última instancia de la Clase System.MarshalByRefObject.
• Tipos cuyas referencias no se calculan en ningún caso a través de dominios AppDomains. Éste
es el valor predeterminado.
Siga estas instrucciones al utilizar el cálculo de referencias por referencia:
• De forma predeterminada, las referencias de instancias se deben calcular con objetos de
valores. Esto significa que sus tipos se deben marcar como Serializable.
• Las referencias de tipos de componentes se deben calcular con objetos de referencia. Éste ya
debería ser el caso de la mayoría de componentes, porque la clase base común o Clase
System.Component, es una clase de cálculo por referencia.
• Si el tipo encapsula un recurso del sistema operativo, las referencias del recurso se deben
calcular con un objeto de referencia. Si el tipo implementa la Interfaz IDisposable es muy
probable que las referencias se calculen por referencia. System.IO.Stream se deriva de
MarshalByRefObject. En la mayoría de las secuencias, como FileStreams y NetworkStreams, se
encapsulan recursos externos para que las referencias se calculen con objetos de referencia.
• En las instancias que simplemente alojan un estado las referencias se deben calcular con
objetos de valor (como DataSet).
• Los tipos especiales que no se pueden llamar a través de un límite AppDomain (como el
propietario de métodos estáticos de utilidades) no se deben marcar como Serializable.
67

Instrucciones para provocar y controlar errores
En las reglas siguientes se describen las instrucciones para provocar y controlar errores:
• Todas las rutas de código que generan una excepción deben proporcionar un método para
comprobar el funcionamiento correcto sin que se inicie una excepción. Por ejemplo, para evitar
una excepción FileNotFoundException, se puede invocar File.Exists; aunque esto puede no
ser siempre posible, pero el objetivo consiste en que no se inicien excepciones en condiciones
de ejecución normales.
• Termine los nombres de la clase Exception con el sufijo Exception como en el siguiente
ejemplo.
[Visual Basic]
Public Class FileNotFoundException
Inherits Exception
' Implementation code goes here.
End Class
[C#]
public class FileNotFoundException : Exception
{ // Implementation code goes here.}
• Al crear clases de excepciones, use los constructores comunes que se muestran en el siguiente
ejemplo de código.
[Visual Basic]
Public Class XxxException
Inherits ApplicationException
Public Sub New()
' Implementation code goes here.
End Sub
Public Sub New(message As String)
' Implementation code goes here.
End Sub
Public Sub New(message As String, inner As Exception)
' Implementation code goes here.
End Sub
Public Sub New(info As SerializationInfo, context As StreamingContext)
' Implementation code goes here.
End Sub
End Class
[C#]
68

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 de los casos, utilice los tipos de excepción predefinidos. Defina nuevos tipos de
excepciones sólo para escenarios en los que espera que los usuarios de la biblioteca de clases
detecten las excepciones de este nuevo tipo y realicen una acción mediante programación en
función del tipo de excepción. Esto es en vez de analizar la cadena de la excepción, lo cual
tendría un impacto negativo en el rendimiento y en el mantenimiento.
Por ejemplo, tiene más sentido definir una excepción FileNotFoundException porque puede
ocurrir que el programador decida crear el archivo que falta. No obstante, no es habitual
administrar una excepción FileIOException en el código.
• No derive todas las excepciones nuevas directamente de la clase base SystemException.
Cuando cree nuevas excepciones en espacios de nombres System, herede sólo de
SystemException. Cuando cree nuevas excepciones en otros espacios de nombres, herede de
ApplicationException.
• Agrupe las nuevas excepciones derivadas de la clase base SystemException o
ApplicationException por espacio de nombres. Por ejemplo, todas las excepciones System.IO
se agrupan bajo IOException (derivada de SystemException) y todas las excepciones
Microsoft.Media se podrían agrupar bajo MediaException (derivada de ApplicationException).
• Utilice una cadena de descripción localizada en cada excepción. Cuando el usuario vea un
mensaje de error, se derivará de la cadena de descripción de la excepción iniciada y nunca de
la clase exception.
• Genere mensajes de error gramaticalmente correctos y con puntuación. Cada frase de la
cadena de descripción de una excepción debe terminar con un punto. El código que
normalmente muestra un mensaje de excepción al usuario, no tiene que tratar el caso en el que
un programador se olvida de incluir un punto final.
• Proporcione las propiedades de la excepción para el acceso mediante programación. Incluya
información adicional (además de la cadena de descripción) en una excepción sólo cuando
haya un escenario mediante programación en el que la información adicional es útil. Serán
contadas las ocasiones en las que necesitará incluir información adicional en una excepción.
• No exponga información protegida en mensajes de excepciones. Datos como las rutas de
acceso del sistema de archivos local se consideran información privilegiada. Código malicioso
podría usar esta información para recopilar información de usuario privada del equipo.
• No utilice excepciones en errores normales o esperados, o en el flujo normal de control.
• Debe devolver el valor null en los casos de error sumamente habituales. Por ejemplo, un
comando File.Open devuelve una referencia null si no encuentra el archivo, pero inicia una
excepción si el archivo está bloqueado.
69

• Diseñe las clases de forma que durante su uso normal nunca se inicie una excepción. En el
siguiente ejemplo de código, una clase FileStream expone otra forma de determinar si se ha
llegado al final del archivo para evitar que se inicie una excepción si el programador lee más allá
del final del archivo.
[Visual Basic]
Class FileRead
Sub Open()
Dim stream As FileStream = File.Open("myfile.txt", FileMode.Open)
Dim b As Byte
' ReadByte returns -1 at end of file.
While b = stream.ReadByte() <> true
' Do something.
End While
End Sub
End Class
[C#]
class FileRead
{ void Open()
{ FileStream stream = File.Open("myfile.txt", FileMode.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 descriptor de
acceso set no es apropiada dado el estado actual del objeto.
• Inicie una excepción ArgumentException o genere una excepción derivada de esta clase si se
pasan o se detectan parámetros no válidos.
• Tenga en cuenta que el seguimiento de la pila comienza en el punto de inicio de una excepción,
y no en el punto en el que se haya creado con el operador new. Considere esto cuando decida
donde iniciar una excepción.
• Utilice los métodos de generador de excepciones. Es normal que una clase inicie la misma
excepción desde distintos lugares de su implementación. Para evitar el código repetitivo, utilice
los métodos auxiliares que crean la excepción y la devuelven utilizando el operador new. En el
siguiente ejemplo de código se muestra cómo implementar un método auxiliar.
[C#]
class File
70

{ string fileName;
public byte[] Read(int bytes)
{
if (!ReadFile(handle, bytes))
throw NewFileIOException();
}

FileException NewFileIOException()
{ string description =
// Build localized string, include fileName.
return new FileException(description);
}
}
• Inicie excepciones en lugar de devolver un código de error o HRESULT.
• Inicie la excepción más específica posible.
• Genere un texto de mensaje descriptivo de las excepciones para el programador.
• Establezca todos los campos en la excepción que utilice .
• Utilice excepciones internas (excepciones encadenadas). Sin embargo, no detecte ni reinicie las
excepciones a menos que agregue más información o cambie el tipo de excepción.
• No cree métodos que inicien NullReferenceException o IndexOutOfRangeException.
• Realice la comprobación de argumentos en miembros protegidos (familia) y en miembros
internos (ensamblado). Indique claramente en la documentación si el método protegido no
realiza una comprobación de argumentos. A menos que se indique lo contrario, suponga que se
realiza la comprobación de argumentos. No obstante, si no se realiza esta comprobación puede
que el rendimiento mejore.
• Limpie cualquier efecto secundario producido al iniciar una excepción. Los llamadores deben
suponer 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 puede asumir que el elemento
especificado no se ha agregado al método Hashtable.
Tipos de excepciones estándar
En la tabla siguiente se incluyen las excepciones estándar que proporciona el tiempo de ejecución y
las condiciones para las que deberá crear una clase derivada.

Tipo de excepción Tipo base Descripción Ejemplo
Exception Object Clase base de
todas las
excepciones.
Ninguno (utilice
una clase
derivada de
esta excepción).
71

SystemException Exception Clase base de
todos los
errores que
genera el motor
de tiempo de
ejecución.
Ninguno (utilice
una clase
derivada de
esta excepción).
IndexOutOfRangeException SystemException La inicia el
motor de
tiempo de
ejecución sólo
cuando no se
indiza
correctamente
una matriz.
Indizar una
matriz fuera del
intervalo válido:
arr[arr.Length+1
]
NullReferenceException SystemException La inicia el
motor de
tiempo de
ejecución sólo
cuando se hace
referencia a un
objeto nulo.
object o = null;
o.ToString();
InvalidOperationException SystemException La inician los
métodos que se
encuentran en
un estado no
válido.
Llamar a
Enumerator.Get
Next() después
de eliminar un
Item de la
colección
subyacente.
ArgumentException SystemException Clase base de
todas las
excepciones de
argumento.
Ninguno (utilice
una clase
derivada de
esta excepción).
ArgumentNullException ArgumentException La inician los
métodos que
no permiten
que un
argumento sea
nulo.
String s = null;
"Calculate".Inde
xOf (s);
ArgumentOutOfRangeException ArgumentException La inician
métodos que
comprueban
que los
argumentos
están en un
intervalo dado.
String s =
"string";
s.Chars[9];
72

ExternalException SystemException Clase base
para
excepciones
que se generan
o que tienen
como destino
entornos fuera
del motor de
ejecución.
Ninguno (utilice
una clase
derivada de
esta excepción).
COMException ExternalException Excepción que
encapsula la
información
Hresult de
COM.
Se usa en la
interoperabilida
d COM.
SEHException ExternalException Excepción que
encapsula la
información de
control
estructurado de
excepciones
Win32.
Se utiliza en la
interoperabilida
d de código no
administrado.

Excepciones de contenedor
Los errores que se generan en el mismo nivel que un componente deben iniciar una excepción
descriptiva para los usuarios de destino. En el siguiente ejemplo de código, el mensaje de error está
destinado a los usuarios de la clase TextReader que intenten leer los datos de una secuencia.
[Visual Basic]
Public Class TextReader
Public Function ReadLine() As String
Try
' Read a line from the stream.
Catch e As Exception
Throw New IOException("Could not read from stream", e)
End Try
End Function
End Class
[C#]
public class TextReader
{ public string ReadLine()
{ try
{ // Read a line from the stream. }
73

catch (Exception e)
{ throw new IOException ("Could not read from stream", e); }
}
}
74

Instrucciones de uso de matrices
Para obtener una descripción general de matrices y del uso de matrices, vea Matrices y
System.Array (Clase).
Matrices y Colecciones
En algunas ocasiones, los diseñadores de bibliotecas de clases tienen que decidir entre utilizar una
matriz o devolver una colección. Aunque estos tipos tienen modelos de uso similares, las
características de rendimiento son diferentes. En general, se debería usar una colección cuando se
pueden utilizar métodos como Add, Remove u otros para manipularla.
Para obtener más información sobre la utilización de colecciones, vea Agrupar datos en
colecciones.
Usar matrices
No se debe devolver una instancia interna de una matriz, ya que esto permite al código llamador
cambiar la matriz. El ejemplo siguiente muestra cómo la matriz badChars puede ser modificada por
cualquier código que tenga acceso a la propiedad Path aunque la propiedad no implemente el
descriptor de acceso set.
[Visual Basic]
Imports System
Imports System.Collections
Imports Microsoft.VisualBasic
Public Class ExampleClass
NotInheritable Public Class Path
Private Sub New()
End Sub
Private Property Path
Get
End Get
Set
End Set
End Property
Private Shared badChars() As Char = {Chr(34),"<"c,">"c}
Public Shared Function GetInvalidPathChars() As Char()
Return badChars
End Function
End Class

Public Shared Sub Main()
75

' The following code displays the elements of the
' array as expected.
Dim c As Char
For Each c In Path.GetInvalidPathChars()
Console.Write(c)
Next c
Console.WriteLine()
' The following code sets all the values to A.
Path.GetInvalidPathChars()(0) = "A"c
Path.GetInvalidPathChars()(1) = "A"c
Path.GetInvalidPathChars()(2) = "A"c
' The following code 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 c
End Sub
End 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 code displays the elements of the
// array as expected.
foreach(char c in Path.GetInvalidPathChars())
{ Console.Write(c); }
Console.WriteLine();

// The following code sets all the values to A.
76

Path.GetInvalidPathChars()[0] = 'A';
Path.GetInvalidPathChars()[1] = 'A';
Path.GetInvalidPathChars()[2] = 'A';
// The following code 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 del ejemplo anterior se puede corregir aplicando a la colección badChars readonly
(ReadOnly en Visual Basic). Como alternativa, se puede clonar la colección badChars antes de
devolverla. En el ejemplo siguiente se muestra cómo modificar el método GetInvalidPathChars para
devolver un clon de 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 deben usar campos readonly (ReadOnly en Visual Basic) de matrices. Si se hace, la matriz
será de sólo lectura y no podrá cambiarse, pero sí se podrán modificar los elementos que la
componen. En el siguiente ejemplo se muestra cómo se pueden cambiar los elementos de la matriz
de sólo lectura InvalidPathChars.
[C#]
public sealed class Path
{ private Path(){}
public static readonly char[] InvalidPathChars = {'\"', '<', '>','|'}'
}
//The following code can be used to change the values in the array.
Path.InvalidPathChars[0] = 'A';
Usar propiedades indizadas en colecciones
Sólo debe utilizar una propiedad indizada como miembro predeterminado de una interfaz o una
clase de la colección. No cree familias de funciones en tipos que no sean una colección. Un modelo
de métodos, como Add, Item y Count, indica que el tipo debe ser una colección.
77

Propiedades con valores de matrices
Utilice las colecciones para evitar que el código funcione mal. En el siguiente ejemplo de código,
cada llamada a la propiedad myObj crea una copia de la matriz. Como resultado, se crearán 2
n
+1
copias de la matriz en el siguiente bucle.
[Visual Basic]
Dim i As Integer
For i = 0 To obj.myObj.Count - 1
DoSomething(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 Propiedades y métodos.
Devolver matrices vacías
Las propiedades String y Array no deben devolver nunca una referencia nula. Este valor puede
resultar difícil de comprender en este contexto. Por ejemplo, un usuario puede suponer que el
siguiente código funcionará.
[Visual Basic]
Public Sub DoSomething()
Dim s As String = SomeOtherFunc()
If s.Length > 0 Then
' Do something else.
End If
End 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 cadenas vacías ("") y las matrices
vacías (0 elementos) deben ser tratadas del mismo modo. Devuelva una matriz vacía en vez de una
referencia nula.
78

Instrucciones de uso para sobrecargar operadores
En las reglas siguientes se describen las pautas para sobrecargar operadores:
• Defina los operadores en tipos de valor que sean tipos de lenguajes lógicos integrados, como la
Estructura System.Decimal.
• Proporcione métodos de sobrecarga de operadores sólo en la clase en la que se definen los
métodos. El compilador de C# cumple esta directriz.
• Utilice las convenciones de firma y de nomenclatura descritas en Common Language
Specification (CLS). El compilador de C# lo hace automáticamente.
• Utilice la sobrecarga de operadores en los casos en los que el resultado de la operación es
obvio. Por ejemplo, tiene sentido poder restar un valor Time de otro valor Time y obtener un
TimeSpan. No obstante, no es adecuado utilizar el operador or para crear la unión de dos
consultas a la base de datos, o utilizar shift para escribir una secuencia.
• Sobrecargue los operadores de forma simétrica. Por ejemplo, si sobrecarga el operador de
igualdad (==), también debe sobrecargar el operador distinto de (!=).
• Proporcione firmas alternativas. La mayoría de los lenguajes no son compatibles con la
sobrecarga de operadores. Por esta razón, es un requisito de CLS que todos los tipos que
sobrecargan operadores incluyan un método secundario con un nombre apropiado específico al
dominio que proporcione la funcionalidad equivalente. La inclusión de un método secundario es
un requisito de Common Language Specification (CLS). El ejemplo siguiente es compatible con
CLS.
[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 de símbolos de operadores y los métodos alternativos y
nombres de operadores correspondientes.

79

Símbolo de
operador C++
Nombre de método alternativo Nombre de operador
No está definido ToXxx o FromXxx op_Implicit
No está definido ToXxx o FromXxx op_Explicit
+ (binario) Add op_Addition
- (binario) Subtract op_Subtraction
* (binario) Multiply op_Multiply
/ Divide op_Division
% Mod op_Modulus
^ Xor op_ExclusiveOr
& (binario) BitwiseAnd op_BitwiseAnd
| BitwiseOr op_BitwiseOr
&& And op_LogicalAnd
|| Or op_LogicalOr
= Assign op_Assign
<< LeftShift op_LeftShift
>> RightShift op_RightShift
No está definido LeftShift op_SignedRightShift
No está definido RightShift op_UnsignedRightShift
== Equals op_Equality
> Compare op_GreaterThan
< Compare op_LessThan
!= Compare op_Inequality
>= Compare op_GreaterThanOrEqual
<= Compare op_LessThanOrEqual
*= Multiply op_MultiplicationAssignment
-= Subtract op_SubtractionAssignment
^= Xor op_ExclusiveOrAssignment
<<= LeftShift op_LeftShiftAssignment
%= Mod op_ModulusAssignment
+= Add op_AdditionAssignment
&= BitwiseAnd op_BitwiseAndAssignment
|= BitwiseOr op_BitwiseOrAssignment
, No hay ninguno asignado op_Comma
/= Divide op_DivisionAssignment
-- Decrement op_Decrement
++ Increment op_Increment
- (unario) Negate op_UnaryNegation
+ (unario) Plus op_UnaryPlus
~ OnesComplement op_OnesComplement
80

Instrucciones para implementar Equals y el operador
de igualdad (==)
En las reglas siguientes se describen las pautas para implementar el método Equals y el operador
de igualdad (==).
• Implemente el método GetHashCode siempre que implemente el método Equals. De este
modo, los métodos Equals y GetHashCode se mantienen sincronizados.
• Reemplace el método Equals siempre que implemente el operador (==), y haga que los dos
realicen las mismas acciones. Esto permite que el código de infraestructuras como Hashtable y
ArrayList, que utilizan el método Equals, se comporten del mismo modo que el código escrito
por el usuario con el operador (==).
• Reemplace el método Equals siempre que implemente la Interfaz IComparable.
• Tenga en cuenta que debe implementar la sobrecarga de operadores para los operadores de
igualdad (==), distinto de (!=), menor que (<), y mayor que (>) cuando implemente
IComparable.
• No inicie excepciones desde los métodos Equals o GetHashCode ni desde el operador de
igualdad (==).
Para obtener más información sobre el método Equals, vea Implementar el método Equals.
Implementar el operador de igualdad (==) en tipos de valor
En la mayoría de los lenguajes de programación no hay un modelo de implementación
predeterminada del operador de igualdad (==) para tipos de valor. Por consiguiente, deberá
sobrecargar el operador (==) cuando sea necesario.
Considere la posibilidad de implementar el método Equals en tipos de valor pues la implementación
predeterminada en System.ValueType no se realiza tan bien como la implementación
personalizada.
Implemente el operador (==) siempre que reemplace el método Equals.
Implementar el operador de igualdad (==) en tipos de referencia
En la mayoría de los lenguajes de programación no hay un modelo de implementación
predeterminada del operador de igualdad (==) para tipos de referencia. Por consiguiente, deberá
tener cuidado al implementar el operador (==) en tipos de referencia. La mayoría de los tipos de
referencia, incluso los que implementan el método Equals, no deben reemplazar el operador (==).
Reemplace el operador (==) si el tipo es un tipo base como Point, String, BigNumber, etc. Siempre
que considere la posibilidad de sobrecargar los operadores de adición (+) y de sustracción (-),
deberá considerar también la sobrecarga del operador (==).
81

Instrucciones de tipos de conversión
En las reglas siguientes se describen las instrucciones de uso de conversiones:
• No permita conversiones implícitas que den como resultado una pérdida de precisión. Por
ejemplo, no debe haber una conversión implícita de Double a Int32, pero puede haber una de
Int32 a Int64.
• No inicie excepciones en conversiones implícitas porque es muy difícil que el programador
comprenda lo que está pasando.
• Proporcione conversiones que funcionen en todo el objeto. El valor que se convierte debe
representar todo el objeto, no un miembro de un objeto. Por ejemplo, no es correcto convertir un
Button en una cadena mediante la devolución de su título.
• No genere un valor que sea semánticamente distinto. Por ejemplo, es conveniente convertir un
valor DateTime o TimeSpan a un valor Int32. El valor Int32 sigue representando el tiempo o la
duración. No tiene sentido, sin embargo convertir una cadena de nombre de archivo como
"c:\mybitmap.gif" a un objeto Bitmap.
• No convierta valores de dominios diferentes. Las conversiones funcionan dentro de un dominio
específico de valores. Por ejemplo, los números y las cadenas pertenecen a distintos dominios.
Tiene sentido convertir un valor Int32 a Double. Sin embargo, no tiene sentido convertir un
valor Int32 a String, ya que estos dos valores se encuentran en dominios diferentes.
82

Modelos de diseño comunes
En este tema se proporcionan las instrucciones de implementación de modelos de diseño comunes
en las bibliotecas de clases.
Implementar Finalize y Dispose para limpiar recursos
no administrados
A menudo, las instancias de clases encapsulan el control sobre los recursos que no administra el
motor de tiempo de ejecución, como identificadores de ventanas (HWND), conexiones a bases de
datos, etc. Por consiguiente, deberá proporcionar las formas explícita e implícita de liberar esos
recursos. Proporcione el control implícito implementando el Método Finalize protegido en un objeto
(sintaxis de destructor de C# y de Extensiones administradas de C++). El recolector de elementos
no utilizados llama a estos métodos en algún momento, cuando ya no hay ninguna referencia válida
al objeto.
En algunos casos, se puede proporcionar a los programadores la utilización de un objeto con
capacidad para liberar explícitamente estos recursos externos, antes de que el recolector de
elementos no utilizados libere el objeto. Si el recurso externo es insuficiente o resulta muy caro, se
puede conseguir un mejor rendimiento si el programador libera explícitamente los recursos cuando
ya no se utilizan. Para proporcionar el control explícito, se implementa el método Dispose que
proporciona la interfaz IDisposable. El consumidor del objeto deberá llamar a este método una vez
hecho esto utilizando el objeto. Se puede llamar al método Dispose aunque todavía existan
referencias al objeto.
Hay que tener en cuenta que cuando se proporciona el control explícito mediante el método
Dispose, se debe proporcionar la limpieza implícita utilizando el método Finalize. Finalize
proporciona una copia de seguridad para evitar que los recursos se pierdan permanentemente si el
programador no llama al método Dispose.
Para obtener más información sobre la implementación de los métodos Finalize y Dispose para
limpiar los recursos no administrados, vea Programar para la recolección de elementos no
utilizados. En el siguiente ejemplo de código se muestra el modelo de diseño básico de
implementación del método Dispose.
[Visual Basic]
' Design pattern for a base class.
Public Class Base
Implements IDisposable
' Implement IDisposable.
Public Overloads Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub

Protected Overloads Overridable Sub Dispose(disposing As Boolean)
83

If disposing Then
' Free other state (managed objects).
End If
' Free your own state (unmanaged objects).
' Set large fields to null.
End Sub
Protected Overrides Sub Finalize()
' Simply call Dispose(False).
Dispose (False)
End Sub
End Class
' Design pattern for a derived class.
Public Class Derived
Inherits Base
Protected Overloads Overrides 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 derived class does not have a Finalize method
' or a Dispose method with parameters 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);
84

}

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# destructor syntax for finalization code.
~Base()
{ // Simply call Dispose(false).
Dispose (false);
}

// Design pattern for a derived class.
public class Derived: Base
{
protected override 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 derived class does not have a Finalize method
// or a Dispose method with parameters because it inherits
// them from the base class.
}
Para obtener un ejemplo de código más detallado del modelo de diseño de implementación de los
métodos Finalize y Dispose, vea Implementar un método Dispose.
Personalizar el nombre del método Dispose
En algunos casos, es más adecuado utilizar un nombre específico del dominio que utilizar Dispose.
Por ejemplo, en la encapsulación de un archivo se puede utilizar Close como el nombre del método.
85

En este caso, se implementa Dispose de forma privada y se crea un método Close público que
llama a Dispose. En el siguiente ejemplo de código se muestra este modelo. Se puede reemplazar
Close con un nombre de método adecuado al dominio.
[Visual Basic]
' Do not make this method overridable.
' A derived class should not be allowed
' to override this method.
Public Sub Close()
' Call the Dispose method with no parameters.
Dispose()
End Sub
[C#]
// Do not make this method virtual.
// A derived class should not be allowed
// to override this method.
public void Close()
{ // Call the Dispose method with no parameters.
Dispose();
}
Finalize
En las reglas siguientes se describen las instrucciones de uso del método Finalize:
• Implemente Finalize sólo en objetos que requieran finalización. Hay algunos costos de
rendimiento asociados con los métodos Finalize.
• Si requiere un método Finalize, considere la posibilidad de implementar IDisposable para que
los usuarios de la clase puedan evitar el costo de invocar el método Finalize.
• No haga más visible el método Finalize. Este método debe ser de tipo protected y no de tipo
public.
• El método Finalize de un objeto debe liberar todos los recursos externos que posea el objeto.
Además, un método Finalize debe liberar sólo los recursos que contiene el objeto. El método
Finalize no debe hacer referencia a ningún otro objeto.
• No llame directamente al método Finalize de un objeto que no sea de la clase base del objeto.
Ésta no es una operación válida en el lenguaje de programación C#.
• Llame al método base.Finalize desde un método Finalize del objeto.
Nota Al método Finalize de la clase base se llama automáticamente con la sintaxis de
destructor de C# y de las Extensiones administradas de C++.
Desechar
En las reglas siguientes se describen las instrucciones de uso del método Dispose:
86

• Implemente el modelo de diseño Dispose en un tipo que encapsula recursos que deben ser
liberados explícitamente. Los usuarios pueden liberar recursos externos llamando al método
Dispose público.
• Implemente el modelo de diseño Dispose en un tipo base que habitualmente tiene tipos
derivados que contienen recursos, aunque la clase base no contenga ninguno. Si el tipo base
contiene un método Close, esto suele indicar que se debe implementar el método Dispose. En
este caso, no implemente un método Finalize en el tipo base. Finalize se debe implementar en
todos los tipos derivados con recursos que es necesario limpiar.
• Libere todos los recursos desechables que contenga un tipo en su método Dispose.
• Una vez llamado el método Dispose en una instancia, evite ejecutar el método Finalize
mediante una llamada al Método GC.SuppressFinalize. La excepción a esta regla son las
situaciones muy poco habituales en las que el trabajo que no realiza el método Dispose se
debe hacer con el método Finalize.
• Llame al método Dispose de la clase base si implementa la interfaz IDisposable.
• No dé por supuesto que se llamará al método Dispose. Los recursos no administrados que
contiene un tipo también se deben liberar en un método Finalize, en el caso de que no se llame
al método Dispose.
• Inicie una excepción ObjectDisposedException a partir de los métodos de instancias de este
tipo (distintos de Dispose) una vez que se haya deshecho de los recursos. Esta regla no se
aplica al método Dispose porque se debería poder llamarlo varias veces sin iniciar una
excepción.
• Propague las llamadas al método Dispose a través de la jerarquía de tipos base. El método
Dispose debe liberar todos los recursos que contenga un objeto y todos los objetos que
contenga ese objeto. Por ejemplo, puede crear un objeto como TextReader que contenga los
objetos Stream y Encoding, los dos creados por TextReader sin el conocimiento del usuario.
Además, Stream y Encoding pueden adquirir recursos externos. Cuando llame al método
Dispose en TextReader, éste deberá llamar a su vez al método Dispose en los objetos Stream
y Encoding, que liberarán los recursos externos.
• Considere la posibilidad de prohibir la utilización de un objeto después de llamar al método
Dispose. La acción de volver a crear un objeto una vez desechado es un modelo difícil de
implementar.
• Permita que se llame al método Dispose más de una vez sin iniciar excepciones. El método no
debe realizar ninguna acción después de la primera llamada.
87

Implementar el método Equals
Para obtener más información sobre la implementación del operador de igualdad (==), vea
Instrucciones para implementar Equals y el operador de igualdad (==).
• Reemplace el método GetHashCode para permitir que un tipo que funcione correctamente en
una tabla hash.
• No inicie una excepción en la implementación de un método Equals. En vez de esto, devuelva
un valor false para un argumento nulo.
• Siga el contrato definido en el Método Object.Equals de la forma siguiente:
• x.Equals(x) devuelve true.
• x.Equals(y) devuelve el mismo valor que y.Equals(x).
• (x.Equals(y) && y.Equals(z)) devuelve el valor true si y solo si x.Equals(z) devuelve el valor
true.
• Las invocaciones sucesivas de x.Equals(y) devuelven el mismo valor siempre que no se
modifiquen los objetos a los que se hace referencia mediante x e y.
• x.Equals(null) devuelve el valor false.
• En algunas clases de objetos, es conveniente realizar una comprobación de Equals para
comprobar así la igualdad de valores en lugar de la igualdad referencial. Este tipo de
implementaciones de Equals devuelve el valor true si los dos objetos tienen el mismo valor,
aunque no sean la misma instancia. La definición de lo que constituye el valor de un objeto está
en función del implementador del tipo, pero suele ser una parte o la totalidad de los datos
almacenados en las variables de la instancia del objeto. Por ejemplo, el valor de una cadena se
basa en los caracteres de la cadena; el método Equals de la clase String devuelve un valor
true en el caso de que dos cadenas de la cadena contengan exactamente los mismos
caracteres y en el mismo orden.
• Cuando el método Equals de una clase base proporciona igualdad de valores, un reemplazo
del método Equals de una clase derivada deberá llamar a la implementación heredada de
Equals.
• Si programa en un lenguaje que es compatible con la sobrecarga de operadores y elige
sobrecargar el operador de igualdad (==) para un tipo especificado, este tipo deberá reemplazar
el método Equals. Este tipo de implementaciones del método Equals debe devolver los mismos
resultados que el operador de igualdad. El seguimiento de esta instrucción garantiza que el
código de la biblioteca de clases que utiliza el método Equals (como ArrayList y Hashtable)
funciona de manera coherente con respecto a la forma en que el código de la aplicación utiliza
el operador de igualdad.
• Si implementa un tipo de valor, considere la posibilidad de reemplazar el método Equals para
obtener un rendimiento mayor que el que proporciona la implementación predeterminada del
método Equals en System.ValueType. Si reemplaza Equals y el lenguaje es compatible con la
sobrecarga de operadores, debe sobrecargar el operador de igualdad del tipo de valor.
• Si implementa tipos de referencia, debe considerar la posibilidad de reemplazar el método
Equals en un tipo de referencia si el tipo de referencia se parece a un tipo base como Point,
String, BigNumber, etc. La mayoría de los tipos de referencia no deben sobrecargar el operador
de igualdad, aunque reemplacen el método Equals. Sin embargo, si implementa un tipo de
88

referencia destinado a contener semántica de valores, como un tipo de número complejo,
deberá reemplazar el operador de igualdad.
• Si implementa la Interfaz IComparable en un tipo dado, deberá reemplazar el método Equals en
ese tipo.
Ejemplos
Implementar el método Equals
El siguiente ejemplo de código contiene dos llamadas a la implementación predeterminada del
método Equals.
[Visual Basic]
Imports System
Class SampleClass
Public Shared Sub Main()
Dim obj1 As New System.Object()
Dim obj2 As New System.Object()
Console.WriteLine(obj1.Equals(obj2))
obj1 = obj2
Console.WriteLine(obj1.Equals(obj2))
End Sub
End 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 del código anterior es el siguiente.
False
True
Reemplazar el método Equals
89

En el siguiente ejemplo de código se muestra una clase Point que reemplaza el método Equals
para proporcionar igualdad de valores y una clase Point3D derivada de Point. Dado que el
reemplazo de la clase Point por parte de Equals es el primero en la cadena de herencia para
introducir la igualdad de valores, no se invoca al método Equals de la clase base (que se hereda de
Object y comprueba la igualdad referencial). No obstante, Point3D.Equals invoca a Point.Equals
porque Point implementa Equals de forma que se proporciona igualdad de valores.
[Visual Basic]
Imports System

Class Point
Private x As Integer
Private y As Integer

Public Overrides 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() Then
Return False
End If
Dim p As Point = CType(obj, Point)
Return Me.x = p.x And Me.y = p.y
End Function
End Class

Class Point3D
Inherits Point
Private z As Integer

Public Overrides Overloads Function Equals(obj As Object) As Boolean
Return MyBase.Equals(obj) And z = CType(obj, Point3D).z
End Function

Public Overrides Function GetHashCode() As Integer
Return MyBase.GetHashCode() ^ z
End Function
End Class
[C#]
using System;
90

class Point: object
{
int x, y;
public override 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 override int GetHashCode()
{
return x ^ y;
}
}

class Point3D: Point
{
int z;
public override bool Equals(Object obj)
{
return base.Equals(obj) && z == ((Point3D)obj).z;
}
public override int GetHashCode()
{
return base.GetHashCode() ^ z;
}
}
El método Point.Equals comprueba que el argumento obj no es null y que hace referencia a una
instancia del mismo tipo que este objeto. Si alguna de las comprobaciones no es satisfactoria, el
método devuelve el valor false. El método Equals utiliza el Método Object.GetType para determinar
si los tipos en tiempo de ejecución de los dos objetos son idénticos. Conviene señalar que typeof
(TypeOf en Visual Basic) no se utiliza aquí porque devuelve el tipo estático. Si en vez de esto el
método ha utilizado una comprobación de la forma obj is Point, la comprobación devolverá el valor
true en los casos en los que obj sea una instancia de una clase derivada de Point, aunque obj y la
instancia actual no tengan el mismo tipo en tiempo de ejecución. Una vez comprobado que ambos
91

objetos son del mismo tipo, el método convierte obj en el tipo Point y devuelve el resultado de la
comparación de las variables de instancia de los dos objetos.
En Point3D.Equals, se invoca al método Equals heredado antes de realizar otras acciones. El
método Equals heredado comprueba si obj no es null, si obj es una instancia de la misma clase que
el objeto y si las variables de la instancia heredada coinciden. Sólo cuando el método Equals
heredado devuelva el valor true, comparará el método las variables de instancia introducidas en la
clase derivada. Concretamente, la conversión a Point3D no se ejecuta salvo que se haya
determinado que obj es de tipo Point3D o una clase derivada de Point3D.
Utilizar el método Equals para comparar variables de instancia
En el ejemplo anterior, el operador de igualdad (==) se utiliza para comparar las variables de
instancia individuales. En algunos casos, debe utilizarse el método Equals para comparar variables
de instancia en una implementación de Equals, como se muestra en el siguiente ejemplo de código.
[Visual Basic]
Imports System

Class Rectangle
Private a, b As Point

Public Overrides Overloads Function Equals(obj As [Object]) As Boolean
If obj Is Nothing Or Not Me.GetType() Is obj.GetType() Then
Return False
End If
Dim 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 Function

Public Overrides Function GetHashCode() As Integer
Return a.GetHashCode() ^ b.GetHashCode()
End Function
End Class
[C#]
using System;
class Rectangle
{
Point a, b;
public override bool Equals(Object obj)
{
92

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 override int GetHashCode()
{
return a.GetHashCode() ^ b.GetHashCode();
}
}
Sobrecargar el operador de igualdad (==) y el método Equals
Algunos lenguajes de programación, como el lenguaje C#, admiten la sobrecarga de operadores.
Cuando un tipo sobrecarga el operador (==), también debe reemplazar el método Equals para
proporcionar la misma funcionalidad. Esto se consigue normalmente escribiendo el método Equals
en términos del operador de igualdad (==) sobrecargado, como en el siguiente ejemplo.
[C#]
public struct Complex
{
double re, im;
public override bool Equals(Object obj)
{
return obj is Complex && this == (Complex)obj;
}
public override int GetHashCode()
{
return re.GetHashCode() ^ im.GetHashCode();
}
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);
}
}
93

Como Complex es un tipo de valor struct de C#, se sabe que no se derivará ninguna clase de
Complex. Por tanto, el método Equals no necesita comparar los resultados de GetType para cada
objeto. En su lugar utiliza el operador is para comprobar el parámetro obj.
94

Uso de las funciones de devolución de llamada
Los Delegados, las Interfaces y los Eventos permiten proporcionar la funcionalidad de devolución de
llamada. Cada tipo tiene sus propias características de uso que se adaptan perfectamente a
situaciones específicas.
Eventos
Utilice un evento si se cumplen las siguientes condiciones:
• Un método solicita por adelantado la función de devolución de llamada, normalmente, a través
de métodos Add y Remove distintos.
• Normalmente, hay más de un objeto que requiere la notificación del evento.
• Desea que los usuarios finales puedan agregar fácilmente un agente de escucha a la
notificación en el diseñador visual.
Delegados
Utilice un delegado si se cumplen las siguientes condiciones:
• Desea un puntero a función con estilo de lenguaje C.
• Desea una función de devolución de llamada sencilla.
• Desea que el registro se produzca en la llamada o durante la construcción y no a través de un
método Add distinto.
Interfaces
Utilice una interfaz si la función de devolución de llamada requiere un comportamiento complejo.
Uso de tiempos de espera
Utilice los tiempos de espera para especificar el tiempo máximo que un llamador está dispuesto a
esperar para finalizar una llamada al método.
Los tiempos de espera pueden adoptar el formato de un parámetro en la llamada al método, como
se muestra a continuación.
[Visual Basic]
server.PerformOperation(timeout)
[C#]
server.PerformOperation(timeout);
Otra posibilidad es utilizar los tiempos de espera como una propiedad de la clase de servidor, como
se muestra a continuación.
[Visual Basic]
server.Timeout = timeout
server.PerformOperation()
[C#]
server.Timeout = timeout;
95

server.PerformOperation();
Conviene utilizar el primer enfoque, porque la asociación entre la operación y el tiempo de espera
es más clara. El enfoque basado en la propiedad puede mejorar si la clase de servidor se diseña
para ser un componente que se utilice con diseñadores visuales.
Tradicionalmente, los tiempos de espera se han representado mediante enteros. Resulta
complicado utilizar números enteros, ya que la unidad del tiempo de espera no es evidente y es
difícil convertir las unidades de tiempo a los milisegundos que se utilizan habitualmente.
El mejor enfoque es utilizar la estructura TimeSpan como tipo de tiempo de espera. TimeSpan
resuelve los problemas con los números enteros de los tiempos de espera mencionados
anteriormente. En el siguiente ejemplo de código se muestra cómo utilizar un tiempo de espera de
tipo TimeSpan.
[Visual Basic]
Public Class Server
Public Sub PerformOperation(timeout As TimeSpan timeout)
' Insert code for the method here.
End Sub
End Class

Public Class TestClass
Dim server As New Server();
server.PerformOperation(New TimeSpan(0,15,0))
End Class
[C#]
public class Server
{
void PerformOperation(TimeSpan timeout)
{
// Insert code for the method here.
}
}

public class TestClass
{
public Server server = new Server();
server.PerformOperation(new TimeSpan(0,15,0));
}
Si el tiempo de espera se establece en TimeSpan(0), el método deberá iniciar una excepción si la
operación no se completa inmediatamente. Si el tiempo de espera es TimeSpan.MaxValue, la
96

operación debe esperar siempre sin que se agote el tiempo de espera, como si no se hubiese
establecido el tiempo de espera. No es necesario que la clase de servidor sea compatible con
ninguno de estos valores, aunque deberá iniciar una InvalidArgumentException si no admite el
valor de tiempo espera especificado.
Si se agota el tiempo de espera y se inicia una excepción, la clase de servidor deberá cancelar la
operación subyacente.
Si se utiliza un tiempo de espera predeterminado, la clase de servidor deberá incluir una propiedad
defaultTimeout estática que se utilizará si el usuario no la especifica. En el siguiente ejemplo de
código se incluye una propiedad OperationTimeout estática de tipo TimeSpan que devuelve
defaultTimeout.
[Visual Basic]
Class Server
Private defaultTimeout As New TimeSpan(1000)

Overloads Sub PerformOperation()
Me.PerformOperation(OperationTimeout)
End Sub

Overloads Sub PerformOperation(timeout As TimeSpan)
' Insert code here.
End Sub

ReadOnly Property OperationTimeout() As TimeSpan
Get
Return defaultTimeout
End Get
End Property
End Class
[C#]
class Server
{
TimeSpan defaultTimeout = new TimeSpan(1000);
void PerformOperation()
{ this.PerformOperation(OperationTimeout); }

void PerformOperation(TimeSpan timeout)
{ // Insert code here. }

97

TimeSpan OperationTimeout
{ get
{ return defaultTimeout; }
}
}
Los tipos que no pueden resolver los tiempos de espera de un TimeSpan deberán redondear el
tiempo de espera al intervalo más cercano. Por ejemplo, un tipo que sólo puede esperar en
incrementos de un segundo deberá redondear al segundo más cercano. La excepción a esta regla
es cuando se redondea un valor a cero. En este caso, el tiempo de espera debe redondearse hacia
arriba al valor de tiempo de espera mínimo posible. Al redondear a un número distinto de cero se
evitan los bucles "busy-wait", en los que un tiempo de espera de valor cero provoca un uso del
procesador del 100 por ciento.
Además, es conveniente iniciar una excepción cuando se agota el tiempo de espera, en vez de
devolver un código de error. Si se agota el tiempo de espera significa que la operación no se ha
podido completar correctamente y, por tanto, se debe tratar y administrar como cualquier otro error
en tiempo de ejecución. Para obtener más información, vea Instrucciones para provocar y controlar
errores.
En el caso de una operación asincrónica con un tiempo de espera, se debe llamar a la función de
devolución de llamada e iniciar una excepción la primera vez que se tiene acceso a los resultados
de la operación. Esto se muestra en el siguiente ejemplo de código:
[Visual Basic]
Sub OnReceiveCompleted(ByVal sender As System.Object, ByVal asyncResult As
ReceiveCompletedEventArgs)
Dim queue As MessageQueue = CType(sender, MessageQueue)
' The following code 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 sender, ReceiveCompletedEventArgs asyncResult)
{
MessageQueue queue = (MessageQueue) sender;
// The following code 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));
98

}
Para obtener más información, vea Instrucciones para programación asincrónica.
99

Seguridad en las bibliotecas de clases
Los diseñadores de bibliotecas de clases deben comprender la seguridad de acceso a código para
escribir bibliotecas de clases seguras. Cuando se escribe una biblioteca de clases, hay que tener en
cuenta dos principios de seguridad: proteger los objetos con permisos y escribir código de plena
confianza. El grado en que se apliquen estos dos principios dependerá de la clase que se escriba.
Algunas clases, como la Clase System.IO.FileStream, representan objetos que es necesario
proteger con permisos. La implementación de estas clases es responsable de comprobar los
permisos de llamadores y de permitir sólo a los llamadores autorizados realizar operaciones para
las que tengan permisos. System.Security (Espacio de nombres) contiene clases que ayudan a
realizar estas comprobaciones en las bibliotecas de clases que se escriban. El código de bibliotecas
de clases a menudo es código de plena confianza o como mínimo de alta confianza. Dado que el
código de bibliotecas de clases tiene acceso con bastante frecuencia a recursos protegidos y código
no administrado, cualquier defecto del código representa una grave amenaza a la integridad de todo
el sistema de seguridad. Para minimizar los peligros relacionados con la seguridad, siga las
instrucciones que se describen en este tema cuando escriba código de bibliotecas de clases. Para
obtener más información, vea Escribir bibliotecas de clases seguras.
Proteger objetos con permisos
Los permisos se definen para proteger recursos específicos. Una biblioteca de clases que realiza
operaciones en recursos protegidos debe exigir esta protección Antes de realizar alguna acción en
la solicitud de un recurso protegido, como eliminar un archivo, el código de la biblioteca de clases
debe comprobar, en primer lugar, que el llamador tiene permiso (y, normalmente, todos los
llamadores, mediante el recorrido de pila) para eliminar el recurso. Si el llamador tiene el permiso
adecuado, permitirá completar la acción. Si el llamador no tiene permiso, la acción no debe
completarse y se debe provocar una excepción de seguridad. La protección se implementa, por lo
general, en el código con una comprobación declarativa o una comprobación imperativa de los
permisos apropiados.
Es importante que las clases protejan los recursos, no sólo contra el acceso directo, sino también
contra cualquier tipo de exposición posible. Por ejemplo, el objeto de un archivo almacenado en la
caché es responsable de comprobar los permisos de lectura del archivo, aunque los datos reales se
recuperen de una caché en memoria y no se produzca ninguna operación real en el archivo. Esto es
así porque el efecto de pasar datos al llamador es el mismo que si el llamador realiza una operación
de lectura real.
Código de biblioteca de clases de plena confianza
En la mayoría de las bibliotecas de clases se implementa código de plena confianza que encapsula
la funcionalidad específica de la plataforma como objetos administrados, por ejemplo, las API del
sistema o de COM. El código de plena confianza puede exponer una debilidad en la seguridad de
todo el sistema. No obstante, esto no debe ocurrir si las bibliotecas de clases se escriben
correctamente con relación a la seguridad, colocando una alta carga de seguridad en un conjunto
de bibliotecas de clases relativamente pequeño; y permitiendo que la parte más extensa del código
administrado adquiera la seguridad principal en tiempo de ejecución de estas bibliotecas de clases
principales.
En un escenario habitual de seguridad de bibliotecas de clases, una clase de plena confianza
expone un recurso protegido mediante un permiso; para tener acceso al recurso se utiliza una API
de código nativo. Un ejemplo típico de este tipo de recurso es un archivo. La Clase File utiliza una
100

API nativa para realizar operaciones en el archivo, como la eliminación. Para proteger el recurso, se
realizan los pasos siguientes.
1. Un llamador solicita la eliminación del archivo c:\test.txt llamando al Método File.Delete.
2. El método Delete crea un objeto de permiso que representa el permiso delete c:\test.txt.
3. El código de la clase File comprueba todos los llamadores de la pila para ver si tienen el
permiso solicitado; si no se les ha concedido el permiso, se provoca una excepción de
seguridad.
4. La clase File declara FullTrust para llamar al código nativo, porque sus llamadores no tienen
este permiso.
5. La clase File utiliza una API nativa para realizar la operación de eliminación del archivo.
6. La clase File devuelve a su llamador, y la solicitud de eliminación del archivo se completa
correctamente.
Precauciones para código de alta confianza
El código de una biblioteca de clases de confianza concede permisos que no están disponibles en el
código de la mayoría de las aplicaciones. Además, un ensamblado puede contener clases que no
necesitan permisos especiales, pero que se conceden porque el ensamblado contiene otras clases
que requieren estos permisos. Estas situaciones pueden exponer una debilidad en la seguridad del
sistema. Por tanto, se debe tener un cuidado especial cuando se escribe código de plena confianza
y código de alta confianza.
Diseñe código de confianza para que se pueda llamar mediante un código de confianza parcial del
sistema sin exponer puntos vulnerables de seguridad. Generalmente, los recursos se protegen
mediante el recorrido de la pila de todos los llamadores. Si un llamador no tiene suficientes
permisos, se bloquea el intento de acceso. Sin embargo, siempre que el código de confianza
declare un permiso, el código tiene la responsabilidad de comprobar los permisos requeridos.
Normalmente, una aserción debe ir seguida de una comprobación de los permisos del llamador
como se ha descrito anteriormente en este tema. Además, se debe minimizar el número de
aserciones de permisos para reducir el riesgo de exposiciones inintencionadas.
Al código de plena confianza se concede implícitamente el resto de los permisos. Además, permite
infringir las reglas de seguridad de tipos y de uso de objetos. Independientemente de la protección
de recursos, cualquier aspecto de la interfaz de programación que pueda descifrar la seguridad de
tipos o permitir el acceso de datos que, por lo general, no están disponibles para el llamador pueden
provocar problemas en la seguridad.
Rendimiento
Las comprobaciones de seguridad incluyen la comprobación de los permisos de los llamadores en
la pila. En función de la profundidad de la pila, estas operaciones pueden tener un costo muy
elevado. Si una operación consiste en realidad en un número de acciones en un nivel inferior en el
que es necesario realizar comprobaciones de seguridad, el rendimiento puede mejorar
enormemente si se comprueban los permisos de los llamadores una vez y, a continuación, se
declara el permiso necesario antes de realizar acciones. Esta aserción detendrá la propagación del
recorrido por la pila en un punto para efectuar correctamente la comprobación. Normalmente, está
técnica da como resultado una mejora del rendimiento si se realizan tres o más comprobaciones de
permisos cada vez.
101

Resumen de los problemas de seguridad de las bibliotecas de clases
• En las bibliotecas de clases que utilizan recursos protegidos se debe garantizar que sólo se
utilizan estos recursos en los permisos de los llamadores.
• La aserción de permisos se debe efectuar sólo cuando es necesario y debe ir precedida de las
necesarias comprobaciones de permisos.
• Para mejorar el rendimiento, agregue operaciones que incluyan comprobaciones de seguridad y
considere la posibilidad de utilizar aserciones para limitar los recorridos de la pila sin poner en
peligro la seguridad.
• Tenga en cuenta que un llamador malicioso de confianza parcial puede en potencia utilizar una
clase para omitir la seguridad.
• No dé por supuesto que sólo los llamadores con permisos específicos llamarán al código.
• No defina interfaces sin seguridad de tipos, pues pueden ser utilizadas para omitir la seguridad
en otros lugares.
• No exponga la funcionalidad en una clase que permita a un llamador de confianza parcial
aprovecharse de la confianza superior de la clase.
102

Operaciones de cadenas que tengan en cuenta las
referencias culturales y la seguridad
Las operaciones de cadena que tienen en cuenta la referencia cultural proporcionada por .NET
Framework pueden resultar ventajosas para los programadores que creen aplicaciones diseñadas
para mostrar resultados basándose en la referencia cultural. De manera predeterminada, los
métodos que tienen en cuenta las referencias culturales obtienen la referencia cultural que van a
usar a partir de la propiedad CultureInfo.CurrentCulture del subproceso actual. Por ejemplo, el
método String.Compare devuelve un resultado que varía según la referencia cultural, debido a las
diferencias en criterios de ordenación y asignaciones de mayúsculas y minúsculas usadas por las
distintas referencias culturales. Sin embargo, las operaciones de cadena que tienen en cuenta la
referencia cultural no son siempre el comportamiento más deseable. Utilizar operaciones que tienen
en cuenta la referencia cultural en escenarios en los que los resultados deberían ser independientes
de ésta puede hacer que el código no funcione en referencias culturales con asignaciones
personalizadas de mayúsculas y minúsculas y reglas de ordenación, y provocar problemas de
seguridad en la aplicación.
Las operaciones con cadenas que tengan en cuenta la referencia cultural pueden crear puntos
vulnerables en la seguridad si el comportamiento esperado por el programador que escribe una
biblioteca de clases difiere del comportamiento real de funcionamiento en el equipo en el que se
ejecuta la operación. Estos cambios de comportamiento pueden producirse si se cambia la
referencia cultural o si la referencia cultural del equipo en el que se ejecuta la operación es distinta
de la que utilizó el programador para probar el código.
Puntos vulnerables en la seguridad en operaciones con
cadenas que tengan en cuenta la referencia cultural
Las exclusivas reglas de asignación de mayúsculas y minúsculas del alfabeto turco muestran cómo
se puede usar una operación que tiene en cuenta la referencia cultural para crear un punto
vulnerable en la seguridad del código de una aplicación. En la mayoría de los alfabetos latinos, el
carácter i (Unicode 0069) es la versión minúscula del carácter I (Unicode 0049). Sin embargo, el
alfabeto turco tiene dos versiones del carácter I: una con un punto y otra sin él. En turco, el carácter
I (Unicode 0049) se considera la versión mayúscula de un carácter diferente &#x0131; (Unicode
0131). Y el carácter i (Unicode 0069) se considera la versión minúscula de otro carácter &#x0130;
(Unicode 0130). Por consiguiente, una comparación de cadenas que no tenga en cuenta las
mayúsculas y minúsculas de los caracteres i (Unicode 0069) e I (Unicode 0049), que tendría éxito
en la mayoría de las referencias culturales, fallaría para la referencia cultural "tr-TR" (turco de
Turquía).
En el siguiente ejemplo de código se muestra cómo una operación String.Compare que no tiene en
cuenta las mayúsculas y minúsculas, realizada sobre las cadenas "FILE" y "file", obtiene resultados
distintos dependiendo de la referencia cultural. La comparación devuelve true si la propiedad
Thread.CurrentCulture se establece como "en-US" (inglés de Estados Unidos). La comparación
devuelve false si CurrentCulture se establece como "tr-TR" (turco de Turquía). Si una aplicación
adopta una decisión de importancia basándose en el resultado de esta operación String.Compare,
el resultado de esa decisión podría verse trastocado si se cambia el valor de CurrentCulture.
[Visual Basic]
Imports System
103

Imports System.Globalization
Imports System.Threading

Public Class TurkishISample
Public 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 Sub
End 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));

104

// 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 pueden cambiar en función de la
referencia cultural, ya que la comparación de i e I que no tiene en cuenta las mayúsculas y
minúsculas se evalúa como true para la referencia cultural "en-US" y como false para la referencia
cultural "tr-TR".
Culture = English (United States)
(file == FILE) = True
Culture = Turkish (Turkey)
(file == FILE) = False
Para obtener más información sobre reglas de ordenación y asignaciones de mayúsculas y
minúsculas personalizadas que puedan provocar incoherencias similares, vea Asignaciones
personalizadas de mayúsculas y minúsculas, y reglas de ordenación.
Especificar la referencia cultural explícitamente en
operaciones de cadenas
El hecho de si las operaciones de cadenas deben tener en cuenta las referencias culturales o no
depende de cómo use los resultados la aplicación. Las operaciones de cadenas que muestran los
resultados al usuario final deberían normalmente tener en cuenta las referencias culturales. Por
ejemplo, si una aplicación muestra al usuario una lista ordenada de cadenas localizadas en un
cuadro de lista, debería realizarse una ordenación que tuviera en cuenta las referencias culturales.
Los resultados de las operaciones de cadenas que se usan internamente no deberían normalmente
tener en cuenta las referencias culturales. En general, si está trabajando con nombres de archivo,
formatos de persistencia o información simbólica que no se muestra al usuario final, los resultados
de las operaciones de cadenas no deberían variar en función de la referencia cultural. Por ejemplo,
si una aplicación compara una cadena para determinar si es una etiqueta XML reconocida, la
comparación no debería tener en cuenta las referencias culturales.
La mayoría de los métodos .NET Framework que realizan operaciones de cadenas que tienen en
cuenta las referencias culturales de manera predeterminada, proporcionan sobrecargas del método
que permiten especificar de forma explícita la referencia cultural que hay que usar, pasando un
parámetro CultureInfo. Use estas sobrecargas para mostrar claramente si se pretende que una
operación de cadenas tenga en cuenta las referencias culturales o no. En operaciones que tienen
en cuenta las referencias culturales, especifique la propiedad CurrentCulture para el parámetro
CultureInfo. Para eliminar los puntos de seguridad vulnerables producidos por variaciones de la
referencia cultural en las reglas de ordenamiento y asignaciones de mayúsculas y minúsculas, se
deben realizar operaciones que no tengan en cuenta la referencia cultural; para ello, basta con
105

especificar la propiedad CultureInfo.InvariantCulture para el parámetro CultureInfo. Esto garantiza
que el código se ejecutará en todos los equipos, sin importar su referencia cultural, con el mismo
comportamiento que durante las pruebas.
Realizar operaciones de cadenas que no distinguen entre
referencias culturales
Las siguientes API de .NET Framework realizan operaciones de cadenas que tienen en cuenta la
referencia cultural de forma predeterminada. Cuando se utilicen estas API, se deberá usar siempre
la sobrecarga de métodos o el constructor de clases que permita especificar de forma explícita la
referencia cultural que se tiene que usar. Para determinar si se debe especificar CurrentCulture
(los resultados tienen en cuenta la referencia cultural) o InvariantCulture (los resultados no tienen
en cuenta la referencia cultural), siga las pautas descritas en Especificar la referencia cultural
explícitamente en operaciones de cadenas.
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) |
CaseInsensitiveHashCodeProvider (Clase) |
SortedList (Clase) |
ArrayList.Sort (Método) |
CollectionsUtil.CreateCaseInsensitiveHashTable (Método) |
Array.Sort (Método) |
Array.BinarySearch (Método) |
System.Text.RegularExpressions (Espacio de nombres)
Los temas siguientes proporcionan más información sobre estas API y ejemplos que muestran
cómo usarlas correctamente para obtener resultados que no tengan en cuenta las referencias
culturales:
• Realizar comparaciones de cadenas que no tienen en cuenta las referencias culturales describe
cómo utilizar los métodos String.Compare y String.CompareTo para llevar a cabo
comparaciones de cadenas que no tengan en cuenta las referencias culturales.
• Realizar cambios de mayúsculas y minúsculas que no tienen en cuenta las referencias
culturales describe cómo utilizar los métodos String.ToUpper, String.ToLower, Char.ToUpper
y Char.ToLower para llevar a cabo cambios de mayúsculas y minúsculas que no tengan en
cuenta las referencias culturales.
• Realizar operaciones de cadenas que no tienen en cuenta las referencias culturales en
colecciones describe cómo utilizar la clase CaseInsensitiveComparer, la clase
CaseInsensitiveHashCodeProvider, la clase SortedList, el método ArrayList.Sort y el
106

método CollectionsUtil.CreateCaseInsensitiveHashtable para llevar a cabo operaciones en
colecciones que no tengan en cuenta las referencias culturales.
• Realizar comparaciones de cadenas que no tienen en cuenta las referencias culturales en
matrices describe cómo utilizar los métodos Array.Sort y Array.BinarySearch para llevar a
cabo operaciones en matrices que no tengan en cuenta las referencias culturales.
• Realizar operaciones que no tienen en cuenta las referencias culturales en el espacio de
nombres describe cómo llevar a cabo operaciones que no tengan en cuenta las referencias
culturales usando métodos en el espacio de nombres RegularExpressions.
Resumen de las instrucciones de uso de cadenas que
tengan en cuenta las referencias culturales
Al realizar operaciones en el código de la biblioteca de clases con cadenas que tengan en cuenta
las referencias culturales, tenga en cuenta las pautas siguientes:
• Pase de forma explícita la información sobre la referencia cultural a todas las operaciones que
tengan en cuenta las referencias culturales. Especifique CultureInfo.CurrentCulture si desea
un comportamiento que tenga en cuenta las referencias culturales. Especifique
CultureInfo.InvariantCulture si desea un comportamiento que no tenga en cuenta las
referencias culturales.
• Use el método String.Compare en lugar del método String.CompareTo. El método
String.Compare clarifica si se desea que una operación tenga en cuenta o no las referencias
culturales. Para examinar ejemplos de código que muestra cómo usar el método
String.Compare, vea Realizar comparaciones de cadenas que no tienen en cuenta las
referencias culturales.
• Proporcione medios para personalizar la referencia cultural de todos los componentes que usen
internamente operaciones que no tengan en cuenta la referencia cultural.
Instrucciones de diseño de subprocesos
En las reglas siguientes se describen las instrucciones de diseño de implementación de
subprocesos:
• Evite proporcionar métodos estáticos que modifiquen el estado estático. En los escenarios de
servidor habituales, el estado estático se comparte entre solicitudes, lo que significa que varios
subprocesos pueden ejecutar ese código al mismo tiempo. Esto abre la posibilidad de errores
en el subproceso. Considere la posibilidad de utilizar un modelo de diseño que encapsule los
datos en instancias que no se compartan entre solicitudes.
• El estado estático debe ser seguro para la ejecución de subprocesos.
• No es necesario que el estado de la instancia sea seguro para la ejecución de subprocesos. De
forma predeterminada, las bibliotecas de clases no deberían ser seguras para la ejecución de
subprocesos. La agregación de bloqueos para crear un código seguro para la ejecución de
subprocesos reduce el rendimiento y aumenta la contención de bloqueos, con lo que se crea la
posibilidad de que se produzcan errores en el bloqueo. En los modelos habituales de
aplicaciones, un solo subproceso ejecuta el código de usuario cada vez, lo que minimiza la
necesidad de seguridad para subprocesos. Por este motivo, las bibliotecas de clases de .NET
Framework no son de forma predeterminada seguras para la ejecución de subprocesos. En los
casos en los que desee proporcionar una versión segura para ejecutar subprocesos,
107

proporcione un método Synchronized estático que devuelva una instancia segura para la
ejecución de subprocesos de un tipo. Para examinar un ejemplo, vea el método
System.Collections.ArrayList.Synchronized y el método
System.Collections.ArrayList.IsSynchronized.
• Diseñe la biblioteca teniendo en cuenta la carga de ejecución en un escenario de servidor. Evite
utilizar bloqueos siempre que sea posible.
• Tenga en cuenta las llamadas a métodos en las secciones bloqueadas. Los bloqueos se
pueden producir cuando el método estático de una clase A llama a los métodos estáticos de la
clase B y viceversa. Si A y B sincronizan sus métodos estáticos, se producirá un bloqueo.
Puede ocurrir que este bloqueo se descubra sólo en condiciones de una gran carga de
subprocesos.
• Se pueden producir problemas de rendimiento cuando un método estático en la clase A llame a
un método estático en la clase A. Si estos métodos no están correctamente factorizados, esto
afectará al rendimiento porque habrá una gran cantidad de sincronización redundante. El uso
excesivo de una sincronización más flexible y detallada puede tener un impacto negativo en el
rendimiento. Además, puede tener un impacto muy negativo en la escalabilidad.
• Tenga en cuenta los problemas con la instrucción lock (SyncLock en Visual Basic). Resulta
más apetecible utilizar la instrucción lock para solucionar todos los problemas de los
subprocesos. Sin embargo, la Clase System.Threading.Interlocked es mejor para las
actualizaciones que deban ser atómicas. Esta clase ejecuta un prefijo lock si no hay
contención. En una revisión de código, tenga cuidado con instancias como la que se muestra en
el ejemplo siguiente.
[Visual Basic]
SyncLock Me
myField += 1
End 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 de un tipo de objeto sólo si es null (Nothing en Visual
Basic). Para actualizar la variable y hacer el código seguro para subprocesos, Se puede usar el
código siguiente.
[Visual Basic]
If x Is Nothing Then
108

SyncLock Me
If x Is Nothing Then
x = y
End If
End SyncLock
End If
[C#]
if (x == null)
{
lock (this)
{
if (x == null)
{
x = y;
}
}
}
Se puede mejorar el rendimiento del 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 de sincronización siempre que sea posible. En las rutas de mucho tráfico, es
mejor evitar la sincronización. Algunas veces se puede ajustar el algoritmo para tolerar las
condiciones de anticipación, en vez de eliminarlas.
109

Instrucciones para programación asincrónica
La programación asincrónica es una función compatible con muchas áreas de Common Language
Runtime, como el Entorno remoto, ASP.NET y los formularios Windows Forms. La programación
asincrónica es un concepto principal en .NET Framework. En este tema se describe el modelo de
diseño de programación asincrónica.
La filosofía de estas instrucciones es como sigue:
• El cliente debe decidir si una determinada llamada debe ser asincrónica.
• No es necesario una programación adicional en el servidor para que sea compatible con el
comportamiento asincrónico del cliente. El motor de tiempo de ejecución debe ser capaz de
administrar la diferencia entre el cliente y el servidor. En consecuencia, se evita la situación en
la que el servidor tiene que implementar IDispatch y realizar una gran cantidad de trabajo para
ser compatible con la invocación dinámica del cliente.
• El servidor puede elegir admitir explícitamente el comportamiento asincrónico para implementar
este comportamiento de forma más eficiente que con una arquitectura general; o para admitir el
comportamiento asincrónico de los clientes. Es conveniente que estos servidores sigan el
modelo de diseño descrito en este documento para exponer operaciones asincrónicas.
• Se debe exigir la seguridad de tipos.
• El tiempo de ejecución proporciona los servicios necesarios para admitir el modelo de
programación asincrónica. Entre estos servicios se incluyen los siguientes:
• Tipos de sincronización primitivos, como las secciones críticas e instancias
ReaderWriterLock.
• Construcciones de sincronización, como contenedores que sean compatibles con el método
WaitForMultipleObjects.
• Grupos de subprocesos.
• Exposición a la infraestructura subyacente, como objetos Message y ThreadPool.
110

Modelo de diseño para programación asincrónica
En el siguiente ejemplo de código se muestra una clase de servidor que factoriza un número.
[Visual Basic]
Public Class PrimeFactorizer
Public Function Factorize(factorizableNum As Long, ByRef primefactor1
As Long, ByRef primefactor2 As Long) As Boolean
primefactor1 = 1
primefactor2 = factorizableNum

' Factorize using a low-tech approach.
Dim i As Integer
For i = 2 To factorizableNum - 1
If 0 = factorizableNum Mod i Then
primefactor1 = i
primefactor2 = factorizableNum / i
Exit For
End If
Next i
If 1 = primefactor1 Then
Return False
Else
Return True
End If
End Function
End Class
[C#]
public class PrimeFactorizer
{
public bool Factorize(long factorizableNum,
ref long primefactor1,
ref long primefactor2)
{
primefactor1 = 1;
primefactor2 = factorizableNum;

111

// Factorize using a low-tech approach.
for (int i=2;i<factorizableNum;i++)
{
if (0 == (factorizableNum % i))
{
primefactor1 = i;
primefactor2 = factorizableNum / i;
break;
}
}
if (1 == primefactor1 )
return false;
else
return true ;
}
}
En el siguiente ejemplo de código se muestra un cliente que define un modelo de invocación
asincrónica al método Factorize de la clase PrimeFactorizer del ejemplo anterior.
[Visual Basic]
' Define the delegate.
Delegate Function FactorizingAsyncDelegate(factorizableNum As Long, ByRef
primefactor1 As Long, ByRef primefactor2 As Long)
End Sub
' Create an instance of the Factorizer.
Dim pf As New PrimeFactorizer()

' Create a delegate on the Factorize method on the Factorizer.
Dim fd As New FactorizingDelegate(pf.Factorize)
[C#]
// Define the delegate.
public delegate bool FactorizingAsyncDelegate(long factorizableNum,
ref long primefactor1,
ref long primefactor2);

// Create an instance of the Factorizer.
PrimeFactorizer pf = new PrimeFactorizer();
112


// Create a delegate on the Factorize method on the Factorizer.
FactorizingDelegate fd = new FactorizingDelegate(pf.Factorize);
El compilador emitirá la clase FactorizingAsyncDelegate siguiente después de analizar la
definición en la primera línea del ejemplo anterior. Esta clase generará los métodos BeginInvoke y
EndInvoke.
[Visual Basic]
Public Class FactorizingAsyncDelegate
Inherits Delegate

Public Function Invoke(factorizableNum As Long, ByRef primefactor1 As
Long, ByRef primefactor2 As Long) As Boolean
End Function

' Supplied by the compiler.
Public Function BeginInvoke(factorizableNum As Long, ByRef primefactor1
As Long, ByRef primefactor2 As Long, cb As AsyncCallback,
AsyncState As Object) As
IasyncResult
End Function

' Supplied by the compiler.
Public Function EndInvoke(ByRef primefactor1 As Long, ByRef
primefactor2 As Long, ar As IAsyncResult) As Boolean
End 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);
113


// Supplied by the compiler.
public bool EndInvoke(ref ulong primefactor1,
ref ulong primefactor2, IAsyncResult ar);
}
La interfaz utilizada como parámetro del delegado en el siguiente ejemplo de código se define en la
biblioteca de clases de .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 delegate 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
114

End Interface
[C#]
public delegate 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 delegate 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) debe ser un objeto temporizador de
espera cuya primitiva de sincronización subyacente debe marcarse una vez cancelada o realizada
la llamada. Esto permite al cliente esperar a que se complete la llamada, en vez de hacer un
sondeo. El tiempo de ejecución proporciona un número de objetos temporizador de espera que
reflejan las primitivas de sincronización de Win32, como ManualResetEvent, AutoResetEvent y
Mutex. También proporciona métodos compatibles con la espera para que los objetos de
sincronización se marquen con la semántica "any" o "all". Estos métodos reconocen el contexto
para evitar bloqueos.
El método Cancel es una solicitud para cancelar el procesamiento del método, una vez finalizado el
tiempo de espera seleccionado. Observe que sólo se recomienda una solicitud del cliente y del
servidor para tenerlo en cuenta. Además, el cliente no debe asumir que el servidor ha detenido la
solicitud de procesamiento totalmente, una vez recibida la notificación de cancelación del método.
Es decir, se recomienda al cliente no destruir recursos como objetos file, ya que se pueden estar
utilizando en el servidor. La propiedad IsCanceled se establecerá en true si se cancela la llamada,
y la propiedad IsCompleted se establecerá en true después de que el servidor complete el
procesamiento de la llamada. Una vez que el servidor establece la propiedad IsCompleted como
true, el servidor no puede utilizar ninguno de los recursos proporcionados por el cliente fuera de la
semántica de uso compartido acordada. De este modo, es seguro para el cliente destruir los
recursos, una vez que la propiedad IsCompleted devuelve el valor true.
115

La propiedad Server devuelve el objeto de servidor proporcionado por IAsyncResult.
En el siguiente código de ejemplo se muestra el modelo de programación en el cliente para invocar
al método Factorize de 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 delegate 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 code.
116

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 delegate.
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 parameters, 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 parameters, you do not need
// the temp variable.
public void FactorizeNumber2()
{
// Client code.
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.
117

ProcessFactorizedNumber fc =
new ProcessFactorizedNumber(factorizableNum);

// Define the AsyncCallback delegate.
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 contexto
sincronizado o de afinidad de subprocesos, la función de devolución de llamada se enviará a través
de la infraestructura del distribuidor de contextos. Es decir, la función de devolución de llamada se
puede ejecutar de forma asincrónica con respecto al llamador de estos contextos. Ésta es la
semántica de un calificador unidireccional de firmas de métodos. Estas llamadas al método se
pueden ejecutar de forma sincrónica o asincrónica con respecto al llamador, y el llamador no puede
suponer nada sobre la finalización de este tipo de llamada cuando se devuelve el control de
ejecución.
Además, si se llama a EndInvoke antes de completar la operación asincrónica, se bloqueará el
llamador. Llamar una segunda vez con igual AsyncResult queda sin definir.
118

Resumen del modelo de diseño para programación asincrónica
El servidor divide una operación asincrónica en dos partes lógicas: la parte que acepta la entrada
del cliente e inicia la operación asincrónica y la parte que proporciona los resultados de la operación
asincrónica para el cliente. Además de la entrada necesaria para la operación asincrónica, la
primera parte también acepta un objeto AsyncCallbackDelegate que se llama después de
completar la operación asincrónica. La primera parte devuelve un objeto temporizador de espera
que implementa la interfaz IAsyncResult utilizada por el cliente para determinar el estado de la
operación asincrónica. Normalmente, el servidor también utiliza el objeto temporizador de espera
que ha devuelto al cliente para mantener cualquier estado asociado con la operación asincrónica. El
cliente utiliza la segunda parte para obtener los resultados de la operación asincrónica
suministrando el objeto temporizador de espera.
Cuando se inician las operaciones asincrónicas, el cliente puede suministrar el delegado de la
función de devolución de llamada, o bien no suministrarlo.
Las siguientes opciones están disponibles en el cliente para completar las operaciones
asincrónicas:
• Sondee el objeto IAsyncResult devuelto para su finalización.
• Intente completar la operación prematuramente, que estará bloqueada hasta que la operación
se complete.
• Espere en el objeto IAsyncResult. La diferencia entre esta opción y la opción anterior es que
se pueden utilizar tiempos de espera periódicamente para volver a tomar el control.
• Complete la operación dentro de la rutina de la función de devolución de llamada.
Un escenario en el que los métodos de lectura y escritura asincrónico y sincrónico son convenientes
es el uso de la entrada/salida de archivos. En el ejemplo siguiente se ilustra el modelo de diseño
que muestra cómo el objeto File implementa las operaciones de 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 As
AsyncCallbackDelegate) As IAsyncResult
Function EndRead(ar As IAsyncResult) As Long

' Synchronous write method.
Function Write(buffer() As [Byte], NumToWrite As Long) As Long

' Asynchrnous write method.
119

Function BeginWrite(buffer() As [Byte], NumToWrite As Long, cb As
AsyncCallbackDelegate) As IAsyncResult
Function EndWrite(ar As IAsyncResult) As Long
End 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 puede asociar fácilmente el estado con una operación asincrónica dada sin definir un
nuevo delegado de la función de devolución de llamada para cada operación. Esto se puede
solucionar haciendo que los métodos Begin, como BeginWrite, adopten un parámetro del objeto
adicional que represente el estado y que se captura en la interfaz IAsyncResult.

Relación con el sistema de tipos común y con Common Language Specification
El Sistema de tipos común es el modelo que define las reglas que se siguen en Common Language Runtime para declarar, utilizar y administrar tipos. Este sistema establece un marco de trabajo que permite la integración entre lenguajes, la seguridad de tipos y ejecutar código de alto rendimiento. Es la materia prima a partir de la cual se pueden crear bibliotecas de clases. Common Language Specification (CLS) define un conjunto de reglas comprobables mediante programación que determina la interoperación de los tipos creados en distintos lenguajes de programación. La selección de CLS es un modo excelente de garantizar la interoperación entre lenguajes. Los diseñadores de bibliotecas de clases administradas pueden utilizar CLS para asegurarse de que las API creadas se pueden llamar desde una amplia gama de lenguajes de programación. Tenga en cuenta que aunque CLS promueve el diseño correcto de bibliotecas, no lo impone. Para obtener más información sobre este tema, vea Escribir código compatible con CLS. Para determinar las funciones que se deben incluir en una biblioteca de clases, siga estos dos principios orientativos con relación a CLS: 1. Determinar si la función facilita el tipo de desarrollo API adecuado al espacio administrado. CLS debe ser lo suficientemente completo como para permitir escribir cualquier tipo de biblioteca administrada. No obstante, si proporciona múltiples formas de realizar una misma tarea, puede desorientar al usuario de la biblioteca de clases a la hora de decidir sobre la utilización y el diseño correctos. Por ejemplo, si incluye construcciones seguras y construcciones no seguras los usuarios deberán elegir entre estas dos opciones. Por consiguiente, CLS fomenta el uso correcto porque ofrece sólo construcciones con seguridad de tipos. 2. Determinar si para el compilador puede ser difícil exponer la función. Todos los lenguajes de programación requerirán algún tipo de modificación para que se adapten al tiempo de ejecución y al sistema de tipos común. Sin embargo, para que los programadores puedan hacer que un lenguaje sea compatible con CLS, no necesitan crear una gran cantidad de trabajo adicional. El objetivo de CLS es ser lo más pequeño posible a la vez que ofrece un extenso conjunto de funciones y tipos de datos.

Instrucciones de nomenclatura
La existencia de un modelo de nomenclatura coherente, es uno de los elementos más importantes en cuanto a previsibilidad y capacidad de descubrimiento en una biblioteca de clases. El uso y el conocimiento generalizados de estas instrucciones de nomenclatura debería eliminar la mayoría de las preguntas más frecuentes de los usuarios. En este tema se proporcionan instrucciones de nomenclatura para los tipos de .NET Framework. En cada tipo, deberá tener en cuenta también algunas de las reglas generales con relación a los estilos de mayúsculas, distinción entre mayúsculas y minúsculas y elección de palabras.

Uso mayúsculas minúsculas Pascal Pascal Pascal Pascal de Pascal de Ejemplo o AppDomain ErrorLevel FatalError ValueChange WebException Nota Termina siempre con el sufijo Exception.Estilos de mayúsculas Utilice las tres convenciones siguientes para poner en mayúsculas los identificadores. En la tabla siguiente se resumen las reglas de uso de mayúsculas y se proporcionan ejemplos de los diferentes tipos de identificadores.Web. puede que sea necesario utilizar mayúsculas en los identificadores para mantener la compatibilidad con esquemas existentes de símbolos no administrados. RedValue Identificador Class Tipo Enum Valores enum Evento Clase excepciones Campo estático de Pascal sólo lectura . donde los caracteres en mayúsculas se utilizan con frecuencia en valores de constantes y enumeraciones. Utilice esta convención sólo para identificadores que estén formados por dos o menos letras. En general. estos símbolos no deben ser visibles fuera del ensamblado en el que se utilizan. El estilo de mayúsculas y minúsculas Pascal se puede utilizar en identificadores de tres o más caracteres. # Mayúsculas y minúsculas Pascal La primera letra del identificador y la primera letra de las siguientes palabras concatenadas están en mayúsculas.IO System. Por ejemplo: System. Por ejemplo: BackColor # Mayúsculas y minúsculas Camel La primera letra del identificador está en minúscula y la primera letra de las siguientes palabras concatenadas en mayúscula. Por ejemplo: backColor Mayúsculas Todas las letras del identificador van en mayúsculas.UI Además.

en vez de un campo de instancia público. en vez de un campo de instancia protegido. .Interfaz Método Espacio nombres Parámetro Propiedad de Pascal Pascal Pascal Camel Pascal IDisposable Nota Comienza siempre con el prefijo I. ToString System. Es preferible utilizar una propiedad.Drawing typeName BackColor redValue Campo de Camel instancia protegido Nota Se utiliza en contadas ocasiones. RedValue Campo de Pascal instancia público Nota Se utiliza en contadas ocasiones. Es preferible utilizar una propiedad.

Windows.cummings. Por ejemplo. En el siguiente ejemplo.POINT p • No debe crear un tipo con nombres de propiedades que difieran sólo en las mayúsculas y minúsculas. • No debe crear una función con nombres de parámetros que difieran sólo en las mayúsculas y minúsculas. un lenguaje que no haga distinción entre mayúsculas y minúsculas no distingue entre las dos declaraciones siguientes de espacio de nombres. namespace ee.Forms. System. siga estas reglas con respecto a la distinción entre mayúsculas y minúsculas: • No utilice nombres que requieran distinción entre mayúsculas y minúsculas. entre mayúsculas y minúsculas.Windows. El siguiente ejemplo es incorrecto. En el siguiente ejemplo. Los lenguajes que no hacen esta distinción no pueden diferenciar.Cummings. set} int COLOR {get. dentro del mismo contexto. No debe crear dos espacios de nombres con nombres que difieran sólo en las mayúsculas y minúsculas. namespace Ee. calculate y Calculate son nombres de métodos incorrectos ya que difieren sólo en el uso de las mayúsculas y minúsculas. y en los que no distinguen. void MyFunction(string a. Por consiguiente. Point p y POINT p son nombres de tipo incorrectos ya que difieren sólo en el uso de las mayúsculas y minúsculas. int Color {get. set} • No debe crear un tipo con nombres de métodos que difieran sólo en las mayúsculas y minúsculas.Forms. dos nombres que difieren sólo en el uso de mayúsculas y minúsculas. se debe evitar esta situación en los componentes o clases creados. En el siguiente ejemplo. string A) • No cree un espacio de nombres con nombres de tipos que difieran sólo en las mayúsculas y minúsculas. void calculate() void Calculate() • . int Color y int COLOR son nombres de propiedades incorrectos ya que difieren sólo en el uso de las mayúsculas y minúsculas.Distinción de mayúsculas y minúsculas Para evitar confusiones y garantizar la interoperación entre lenguajes. Los componentes se deben poder utilizar en los lenguajes que distinguen.Point p System.

Addhandler As ByRef AddressOf Assembly Byte Alias Auto ByVal And Base Call Ansi Boolean Uso mayúsculas minúsculas CDate Class CSng Declare Double Enumeración ExternalSource For GoTo In de o Catch CDec CLng CStr Default Each Erase False Friend Handles CBool CDbl CObj CType Delegate Else Error Finalize Función If CByte Char Const Date Dim ElseIf Evento Finally Get Implements CChar CInt CShort Decimal Do End Exit Float GetType Imports . Por ejemplo.NET Framework.NET Framework. utilice el estilo de mayúsculas y minúsculas Pascal o Camel en acrónimos de más de dos caracteres. Por ejemplo. utilice las Mayúsculas y minúsculas Camel en abreviaturas de dos o más caracteres. No utilice acrónimos que no estén aceptados en el campo de la informática. aunque esto contradiga la abreviatura estándar de la palabra. Sin embargo. Cuando utilice acrónimos.IO en vez de System. evite utilizar identificadores que entren en conflicto con las siguientes palabras clave. siga estas reglas con respecto a la utilización de abreviaturas: • • • • No utilice abreviaturas ni contracciones como parte de nombres de identificadores. Collections. Por ejemplo. Si es necesario.Io. use HtmlButton o htmlButton. por ejemplo System. Si tiene que utilizar abreviaturas. deberá utilizar mayúsculas en acrónimos que tengan sólo dos caracteres. utilice GetWindow en vez de GetWin. No utilice abreviaturas en nombres de identificadores o parámetros. no utilice ninguno de los nombres siguientes como nombres de clases: System. • Elección de palabra Evite utilizar nombres de clases que dupliquen los espacios de nombres utilizados habitualmente en .Abreviaturas Para evitar confusiones y garantizar la interoperación entre lenguajes. Por ejemplo. Además. utilice UI para interfaz de usuario y OLAP para procesamiento analítico en línea. utilice acrónimos conocidos para reemplazar nombres en frases largas. Forms o UI. Vea Biblioteca de clases para obtener una lista de los espacios de nombres de .

Inherits Lib Mod MyClass Nothing Opción Overrides Protected Region Select Single Structure A Until WithEvents instanceof Integer Like Module Namespace NotInheritable Opcional ParamArray Public REM Set Static Sub True volatile WriteOnly package Interfaz Long MustInherit Nueva NotOverridable Or Preserve RaiseEvent RemoveHandler Shadows Paso SyncLock Try When Xor var Is Loop MustOverride Next Object Overloads Private ReadOnly Resume Shared Stop Then TypeOf While eval Let Me MyBase Not On Overridable Property ReDim Return Short String Throw Unicode With extends .

[Visual Basic] Sub Write(doubleValue As Double). Sub Write(value As Long). [C#] void Write(double value).Evitar confusión de nombres de tipos Los distintos lenguajes de programación utilizan términos diferentes para identificar los tipos administrados básicos. No cree nombres de métodos con terminología específica del lenguaje. utilice un nombre genérico. Utilice nombres que describan el significado del tipo. void Write(float floatValue). Los diseñadores de bibliotecas de clases deben evitar utilizar terminología específica del lenguaje. Sub Write(singleValue As Single). void Write(long value). void Write(int value). . Sub Write(value As Single). en vez de nombres que describan el tipo. Sub Write(value As Short). Sub Write(value As Integer). Siga las reglas que se describen en esta sección para evitar la confusión de nombres de tipos. una clase que admite la escritura de diversidad de tipos de datos en una secuencia puede tener los métodos siguientes. [C#] void Write(double doubleValue). Sub Write(longValue As Long). En el caso poco frecuente de que un parámetro no tenga ningún otro significado semántico aparte del significado del tipo. void Write(float value). como se muestra en el siguiente ejemplo. Por ejemplo. void Write(long longValue). Sub Write(integerValue As Integer). void Write(int intValue). void Write(short value). [Visual Basic] Sub Write(value As Double). Sub Write(shortValue As Short). void Write(short shortValue).

[Visual Basic] ReadDouble()As Double ReadSingle()As Single ReadInt64()As Long ReadInt32()As Integer ReadInt16()As Short [C#] double ReadDouble().exe Basic C++ SByte Byte Short UInt16 Integer UInt32 Long UInt64 Single Double Boolean Char String Object sByte byte short ushort int uint long ulong float double boolean char string object char int8 Nombre de tipo universal SByte Byte Int16 UInt16 Int32 UInt32 Int64 UInt64 Single Double Boolean Char String Object unsigned char unsigned int8 short unsigned short int unsigned int __int64 unsigned __int64 float double bool wchar_t String Object int16 unsigned int16 int32 unsigned int32 int64 unsigned int64 float32 float64 bool char string object Por ejemplo. float ReadSingle(). utilice nombres de tipos universales. int ReadInt32().En el caso excepcional de que sea necesario crear un método con un nombre único para cada tipo básico de datos. . short ReadInt16(). long ReadInt64(). En la tabla siguiente se muestran los nombres de tipos básicos y las sustituciones universales. Nombre de tipo C# sbyte byte short ushort int uint long ulong float double bool char string object Nombre de Nombre de Nombre de Representación tipo Visual tipo JScript tipo Visual Ilasm. una clase que admita la lectura de diversidad de tipos de datos en una secuencia puede tener los siguientes métodos.

en vez de la alternativa específica de lenguaje que se muestra a continuación. . short ReadShort(). long ReadLong(). [Visual Basic] ReadDouble()As Double ReadSingle()As Single ReadLong()As Long ReadInteger()As Integer ReadShort()As Short [C#] double ReadDouble(). int ReadInt().Es preferible utilizar el ejemplo anterior. float ReadFloat().

Media Microsoft. Por ejemplo. .IOs.Design dependen de las clases de System. Un espacio de nombres anidado debe tener una dependencia en los tipos del espacio de nombres contenedor. Por ejemplo.Feature][. se debe utilizar el estilo de Mayúsculas y minúsculas Pascal y separar los componentes lógicos con puntos.IO en vez de System.Design] Por ejemplo: Microsoft.Office es un prefijo adecuado para las clases de automatización de Office que proporciona Microsoft. se evita la posibilidad de publicar dos espacios de nombres que tengan el mismo nombre. Asigne un nombre a un espacio de nombres que contenga los tipos que proporcionan funcionalidades en tiempo de diseño para un espacio de nombres base con el sufijo .Forms.UI. Por ejemplo. En los espacios de nombres. la característica y el diseño como se muestra a continuación.Instrucciones de nomenclatura de espacios de nombres Por regla general. como en Microsoft.PowerPoint. las clases de System.WebObjects y ee. No obstante. en los espacios de nombres se utiliza el nombre de la compañía seguido del nombre de la tecnología y.Windows.TechnologyName[.Web. los espacios de nombres NeXT. Por ejemplo.Office.MyTechnology.Web.Design Al incluir un prefijo en los nombres de espacios de nombres que contengan el nombre de una compañía o marca de reconocido prestigio. opcionalmente.dll a un ensamblado.Design.Design.Collection.Forms. no proporcione un espacio de nombres Debug y una clase Debug.Web. Por ejemplo. Si la marca utilizada no emplea la regla de mayúsculas y minúsculas tradicional.UI. Utilice una jerarquía organizativa como base para la jerarquía de espacios de nombres.Windows.UI. CompanyName.cummings muestran las variaciones correctas de utilización de mayúsculas y minúsculas con respecto a la regla Pascal.Web.Collections en vez de System. si asigna el nombre MyCompany. Por último. aunque no siga la regla Pascal. utilice System.MyTechnology. siempre que sea correcto semánticamente. Por ejemplo. No utilice el mismo nombre para un espacio de nombres y para una clase. el Espacio de nombres System. Por ejemplo. Microsoft. utilice System. las clases de System.Design contiene las clases de diseñador y clases relacionadas para diseñar aplicaciones basadas en System.UI no dependen de las clases en System. Utilice un nombre de tecnología reconocida y estable en el segundo nivel de un nombre jerárquico. no es necesario que contenga un espacio de nombres MyCompany.Media. Por ejemplo. siga el sistema que utiliza la marca. Las excepciones a esta regla son los nombres y abreviaturas de marcas. Utilice los nombres de espacios de nombres en plural. tenga en cuenta que el nombre de un espacio de nombres no tiene que ser análogo al nombre del ensamblado.

IdentityStore es un nombre de clase correcto. Por ejemplo. utilice una palabra compuesta en el nombre de una clase derivada. Utilice las abreviaturas con moderación. No utilice el carácter de subrayado (_). por ejemplo. • A continuación. Por ejemplo. el nombre de clase FileStream en vez de CFileStream. se incluyen algunos ejemplos de clases con nombres correctos: [Visual Basic] Public Class FileStream Public Class Button Public Class String [C#] public class FileStream public class Button public class String . Button es un nombre adecuado para una clase derivada de Control. Esto es correcto siempre que I sea la primera letra de una palabra que forme parte del nombre de la clase. como C para clase. Por ejemplo. aunque la clase no sea una interfaz. Cuando sea apropiado. Utilice. en un nombre de clase. es necesario proporcionar un nombre de clase que comience con la letra I. si incluye Control como parte del nombre de una clase alargaría el nombre innecesariamente. En esta regla se debe utilizar la lógica. La segunda parte del nombre de la clase derivada debe ser el nombre de la clase base.Instrucciones de nomenclatura de clases En las reglas siguientes se describen las instrucciones de nomenclatura de clases: • • • • • • Utilice un sustantivo o un sintagma nominal para asignar un nombre a una clase. No utilice un prefijo de tipo. De vez en cuando. ApplicationException es un nombre correcto para una clase derivada de la clase Exception. pues ApplicationException es una clase de Exception. Aunque un botón es una clase de control. Utilice el estilo de Mayúsculas y minúsculas Pascal.

Utilice el estilo de Mayúsculas y minúsculas Pascal.Instrucciones de nomenclatura de interfaces En las reglas siguientes se describen las pautas de nomenclatura de interfaces: • Asigne nombres a interfaces utilizando sustantivos. [Visual Basic] Public Interface IComponent ' Implementation code goes here. Incluya un prefijo con la letra I en los nombres de interfaces para indicar que el tipo es una interfaz. End Class [C#] public interface IComponent {// Implementation code goes here. donde la clase es una implementación estándar de la interfaz. Por ejemplo. En el nombre IPersistable se utiliza un adjetivo. Los nombres deben ser distintos sólo en el prefijo I del nombre de la interfaz. sintagmas nominales o adjetivos que describan su comportamiento. en el nombre de la interfaz IComponent se utiliza un sustantivo descriptivo. No utilice el carácter de subrayado (_).} . Utilice nombres similares cuando defina un par clase/interfaz. se incluyen algunos ejemplos de interfaces con nombres correctos: [Visual Basic] Public Interface IServiceProvider Public Interface IFormatable [C#] public interface IServiceProvider public interface IFormatable En el siguiente ejemplo de código se muestra cómo definir la interfaz IComponent y su implementación estándar.} public class Component: IComponent {// Implementation code goes here. End Interface Public Class Component Implements IComponent ' Implementation code goes here. En el nombre de la interfaz ICustomAttributeProvider se utiliza un sintagma nominal. • • • • • A continuación. la clase Component. Utilice las abreviaturas con moderación.

No utilice un prefijo de notación húngara en nombres de campos estáticos. Instrucciones de nomenclatura de campos estáticos En las reglas siguientes se describen las instrucciones de nomenclatura de campos estáticos: • • • • Utilice sustantivos. se incluye un ejemplo de un nombre correcto de clase de atributo: [Visual Basic] Public Class ObsoleteAttribute [C#] public class ObsoleteAttribute{} Instrucciones enumeración • • • • • de nomenclatura de tipos de El tipo de valor de enumeración (Enum) se hereda de la Clase Enum. pero utilice un nombre en plural para los tipos Enum que son campos de bits. En las reglas siguientes se describen las instrucciones de nomenclatura de enumeraciones: Utilice el estilo de Mayúsculas y minúsculas Pascal en los nombres de valores y tipos Enum. A continuación. Utilice las abreviaturas con moderación. Utilice un nombre en singular para la mayoría de los tipos Enum. No utilice el sufijo Enum en nombres de tipo Enum. sintagmas nominales o abreviaturas de nombres al asignar nombres a campos estáticos. . Agregue siempre FlagsAttribute a un tipo Enum de campo de bits. Utilice el estilo de Mayúsculas y minúsculas Pascal.Instrucciones de nomenclatura de atributos Deberá agregar siempre el sufijo Attribute a las clases de atributos personalizados. Se recomienda utilizar propiedades estáticas en lugar de campos estáticos públicos cada vez que sea posible.

Por ejemplo. Utilice nombres de parámetros descriptivos. el nombre del parámetro también sirve para describir su significado.Instrucciones de nomenclatura de parámetros Es importante seguir estas instrucciones de nomenclatura de parámetros. En vez de esto. ya que las herramientas de diseño visual que proporcionan ayuda contextual y funcionalidad de exploración de clases muestran en el diseñador los nombres de los parámetros de métodos a los usuarios. si así se precisa. Utilice los nombres de parámetros basados en tipos con moderación y sólo cuando sea correcto. No utilice parámetros reservados. Utilice nombres que describan el significado del parámetro. args() As object)As String [C#] Type GetType(string typeName) string Format(string format. las herramientas de diseño visual que proporcionan ayuda contextual muestran los parámetros de los métodos a los programadores mientras escriben. Por tanto. • • • A continuación. los nombres de los parámetros deberían ser lo suficientemente descriptivos en este escenario como para permitir a los programadores suministrar los parámetros adecuados. No incluya un prefijo en los nombres de parámetros con notación húngara de tipo. En las reglas siguientes se describen las instrucciones de nomenclatura de parámetros: • • Utilice el estilo de Mayúsculas y minúsculas Camel para los nombres de parámetros. Los parámetros reservados son parámetros privados que se pueden exponer en futuras versiones. agregue una nueva sobrecarga para un método. Los nombres de parámetros deben ser lo suficientemente descriptivos como para que el nombre y el tipo del parámetro se puedan utilizar para determinar su significado en la mayoría de los escenarios. se incluyen algunos ejemplos de parámetros con nombres correctos: [Visual Basic] GetType(typeName As String)As Type Format(format As String. object[] args) . si se necesitan más datos en una versión futura de la biblioteca de clases. Por tanto. en vez de nombres que describan el tipo de parámetro. Las herramientas de desarrollo deben proporcionar información descriptiva sobre el tipo de parámetro.

[Visual Basic] Public Class SampleClass Public Property BackColor As Color ' Code for Get and Set accessors goes here. Utilice el estilo de Mayúsculas y minúsculas Pascal. [Visual Basic] Public Enum Color ' Insert code for Enum here. } } En el siguiente ejemplo de código se muestra cómo proporcionar una propiedad con el mismo nombre que el tipo.Instrucciones de nomenclatura de métodos En las reglas siguientes se describen las instrucciones de nomenclatura de métodos: • • Utilice verbos o sintagmas verbales al asignar nombres a los métodos. si declara una propiedad con el nombre Color. Por ejemplo. Utilice el estilo de Mayúsculas y minúsculas Pascal. el tipo de propiedad también deberá llamarse Color. Es conveniente crear una propiedad con el mismo nombre que el tipo subyacente correspondiente. End Property End Class [C#] public class SampleClass { public Color BackColor { // Code for Get and Set accessors goes here. End Enum . En el siguiente ejemplo de código se muestra cómo asignar nombres correctos a propiedades. No utilice la notación húngara. A continuación. se incluyen algunos ejemplos de métodos con nombres correctos: RemoveAll() GetCharArray() Invoke() Instrucciones de nomenclatura de propiedades En las reglas siguientes se describen las instrucciones de nomenclatura de propiedades: • • • • Utilice un sustantivo o un sintagma nominal al asignar nombres a las propiedades. Vea el ejemplo que se muestra más adelante en este tema.

} set {// Insert code here.Public Class Control Public Property Color As Color Get ' Insert code here. [Visual Basic] Public Enum Color ' Insert code for Enum here.} public class Control { public Color Color { get {// Insert code here. End Get Set ' Insert code here.} } } El siguiente ejemplo de código es incorrecto ya que la propiedad Color es de tipo Integer. End Enum Public Class Control Public Property Color As Integer Get ' Insert code here.} . End Set End Property End Class [C#] public enum Color {// Insert code for Enum here. End Get Set ' Insert code here. End Set End Property End Class [C#] public enum Color { // Insert code for Enum here.

Especifique dos parámetros denominados sender y e. [Visual Basic] Public Delegate Sub MouseEventHandler(sender As Object. Instrucciones de nomenclatura de eventos En las reglas siguientes se describen las instrucciones de nomenclatura de eventos: • • • • Utilice el estilo de Mayúsculas y minúsculas Pascal. En general.public class Control { public int Color { get {// Insert code here. Este método debe tener sólo el parámetro de evento e. Asigne un nombre a la clase de argumentos del evento con el sufijo EventArgs.} set {// Insert code here.Int32). un evento Close que se puede cancelar. • • • • • En el ejemplo siguiente se muestra un controlador de eventos con nombre y parámetros correctos. Painting y DroppedDown. Por ejemplo. nombres de eventos correctos incluyen Clicked. Color. se debe proporcionar un método protegido denominado OnXxx en los tipos con eventos que se pueden reemplazar en una clase derivada. No utilice el modelo de nomenclatura BeforeXxx/AfterXxx. Por ejemplo.} } } En el ejemplo incorrecto. Por ejemplo. Es conveniente asignar los nombres de eventos con un verbo. Utilice un sufijo EventHandler en nombres de controladores de eventos. Use una clase de eventos apropiada y específica para el tipo de parámetro e. e As MouseEventArgs) [C#] . El estado asociado con el evento está encapsulado en una instancia de una clase de eventos denominada e. a continuación. ya que el remitente es siempre la instancia del tipo. El parámetro sender es siempre de tipo object. aunque sea posible utilizar un tipo más específico. debe tener un evento Closing y un evento Closed. utilice Close en vez de OnClose. Utilice un verbo en gerundio para crear un nombre de evento que exprese el concepto de evento anterior. tiene acceso a un miembro de ese valor (que tendrá que ser un miembro de instancia de System. y un tiempo en pasado para representar un evento posterior. El parámetro sender representa al objeto que produjo el evento. No utilice prefijo ni sufijo en la declaración de evento en el tipo.Xxx se interpretará como que tiene acceso a un miembro que primero obtiene el valor de la propiedad Color (tipo Integer en Visual Basic o tipo int en C#) y que. No utilice la notación húngara. no es posible hacer referencia a los miembros de la enumeración Color.

y = y. } public int X { get { return x. public MouseEventArgs(int x. En el siguiente ejemplo se muestra una clase de argumentos de evento con nombre correcto. } } public int Y { get { return y. int y. y As Integer) public class MouseEventArgs : EventArgs { int x. MouseEventArgs e).x = x. this.public delegate void MouseEventHandler(object sender. int y) { this. [Visual Basic] Public Class MouseEventArgs Inherits EventArgs Dim x As Integer Dim y As Integer Public me.y = y End Sub Public Property X As Integer Get Return x End Get End Property Public Property Y As Integer Get Return y End Get End Property End Class [C#] Sub New MouseEventArgs(x As Integer. } } } .x = x me.

el código anterior es equivalente a lo siguiente: .DataField = "AuthorID". se garantiza que los datos no se perderán si el descriptor de acceso set inicia una excepción. conserve el valor de la propiedad antes de cambiarlo. DataSource especifica el nombre de la tabla y DataField el nombre de la columna. t. Las propiedades deben ser independientes con relación a otras propiedades. un control TextBox puede contener dos propiedades relacionadas: DataSource y DataField. la función no se activa. Cuando tenga acceso a una propiedad mediante el descriptor de acceso set. Una vez especificadas las dos propiedades. Cuando el objeto tenga el estado correcto. el control puede enlazar automáticamente datos de la tabla a la propiedad Text del control. Para obtener más información sobre la elección entre propiedades o métodos.DataSource = "Publishers". Si el objeto no tiene el estado correcto.DataField = "AuthorID" ' The data-binding feature is now active. Las propiedades DataSource y DataField se pueden establecer en cualquier orden. [C#] TextBox t = new TextBox(). Por ejemplo. Elija un nombre para la propiedad siguiendo las Instrucciones de nomenclatura de propiedades recomendadas. o hasta que el objeto tiene un estado determinado. o de cómo consiga que el objeto tenga el estado activo. Instrucciones de uso de propiedades Determina si es más adecuado utilizar una propiedad o un método en función de las necesidades.DataSource = "Publishers" t. Por tanto. La semántica es la misma independientemente del orden en el que el programador establezca los valores de la propiedad. t. En el siguiente ejemplo de código se muestran las propiedades que se pueden establecer en cualquier orden. // The data-binding feature is now active. De este modo. Problemas de estado de propiedades Permita que las propiedades se establezcan en cualquier orden. vea Propiedades y métodos.Instrucciones de uso de miembros de clases En este tema se proporcionan las instrucciones de utilización de miembros de clases en las bibliotecas de clases. [Visual Basic] Dim t As New TextBox() t. la función se activará automáticamente sin necesidad de una llamada explícita. Suele ocurrir a menudo que la función de un objeto no se activa hasta que el programador define un conjunto de propiedades específico.

DataField = "AuthorID".DataField = "AuthorID". [C#] TextBox t = new TextBox(). En el siguiente ejemplo de código se muestra cómo realizar el seguimiento del estado de la función de enlace de datos y activarla o desactivarla automáticamente en el momento adecuado. // The data-binding feature is now inactive. [Visual Basic] Public Class TextBox Private m_dataSource As String Private m_dataField As String Private m_active As Boolean Public Property DataSource() As String . t.DataSource = "Publishers" ' The data-binding feature is now active. [Visual Basic] Dim t As New TextBox() t.DataSource = "Publishers". [C#] TextBox t = new TextBox(). // The data-binding feature is now active. t.DataSource = null. t. t.DataSource = Nothing ' The data-binding feature is now inactive.DataSource = "Publishers".DataSource = "Publishers" ' The data-binding feature is now active.[Visual Basic] Dim t As New TextBox() t.DataField = "AuthorID" t.DataField = "AuthorID" t. También se puede establecer una propiedad como null (Nothing en Visual Basic) para indicar que no se especifica ningún valor. t. t. // The data-binding feature is now active.

SetActive(( Not (m_dataSource Is Nothing) And Not (m_dataField Is Nothing))) End If End Set End Property Sub SetActive(m_value As Boolean) If value <> m_active Then If m_value Then Activate() Text = dataBase. m_dataSource = value ' Update active state. m_dataField = value ' Update active state.Value(m_dataField) Else Deactivate() Text = "" End If ' Set active only if successful. SetActive(( Not (m_dataSource Is Nothing) And Not (m_dataField Is Nothing))) End If End Set End Property Public Property DataField() As String Get Return m_dataField End Get Set If value <> m_dataField Then ' Set the property value first. m_active = value .Get Return m_dataSource End Get Set If value <> m_dataSource Then ' Set the property value first. in case activate fails. in case activate fails.

End Sub Sub Deactivate() ' Close database. End Sub End Class [C#] public class TextBox { string dataSource. } set { if (value != dataSource) { // Update active state. bool active. } } } public string DataField { . SetActive(value != null && dataField != null).End If End Sub Sub Activate() ' Open database. dataSource = value. public string DataSource { get { return dataSource. string dataField.

} // Set active only if successful. } } . Text = "". Text = dataBase. dataField = value. } void Deactivate() { // Close database. } set { if (value != dataField) { // Update active state. } } void Activate() { // Open database. active = value. } else { Deactivate(). } } } void SetActive(Boolean value) { if (value != active) { if (value) { Activate().Value(dataField).get { return dataField. SetActive(dataSource != null && dataField != null).

BeginInit e ISupportInitialize. Para obtener más información sobre la elección entre propiedades o métodos. Por ejemplo. La convención de nomenclatura para un evento PropertyChanged es agregar el sufijo Changed al nombre de la propiedad. como en TextChanged. la siguiente expresión determina si el objeto está en un estado en el que la función de enlace de datos se activa automáticamente.En el ejemplo anterior. es posible que la sobrecarga para provocar el evento PropertyChanged y agregar un elemento a la tabla hash no compense. un control puede provocar un evento TextChanged cuando se cambia la propiedad de texto. a continuación. string dataField) { SetActive(dataSource != null && dataField != null). si se desea notificar a los consumidores cuando se cambia la propiedad del componente mediante programación. se activa si es necesario.EndInit al establecer varias propiedades para que el componente pueda proceder a optimizar. como DataSource y DataMember. se puede utilizar la rutina auxiliar protegida Raise<Property>Changed. De este modo. [Visual Basic] Sub UpdateActive(m_dataSource As String. vea Propiedades frente a métodos. ISupportInitialize puede evitar los intentos innecesarios de obtener acceso a la base de datos hasta que se haya realizado correctamente la configuración. [Visual Basic] Class Control Inherits Component Private m_text As String Public Property Text() As String . En el ejemplo anterior. afecta a las propiedades DataSource y DataField. En este caso. Para provocar este evento.} Si tiene propiedades relacionadas. La expresión que aparece en este método indica las partes del modelo de objetos que se deben examinar para exigir estas transiciones de estado. Provocar eventos PropertyChanged Los componentes deben provocar eventos PropertyChanged. En el siguiente ejemplo de código se muestra la implementación de una rutina auxiliar en un evento PropertyChanged. No obstante. el diseñador (o el usuario) podrá llamar a los métodos ISupportInitialize. considere la posibilidad de implementar ISupportInitialize (Interfaz). m_dataField As String) SetActive(( Not (m_dataSource Is Nothing) And Not (m_dataField Is Nothing))) End Sub [C#] void UpdateActive(string dataSource. [Visual Basic] (Not (value Is Nothing) And Not (m_dataField Is Nothing)) [C#] value != null && dataField != null La activación automática se define mediante la creación de un método que determina si el objeto se puede activar en función de su estado actual y.

la propiedad se actualiza. Conviene provocar eventos de cambio/cambiados. . el enlace de datos funciona en un sentido. Cada propiedad que provoca el evento <Property>Changed debe proporcionar los metadatos que indican que la propiedad admite el enlace de datos. } } } } El enlace de datos utiliza este modelo para permitir el enlace de la propiedad en ambos sentidos. public string Text { get { return text.Equals(value) Then m_text = value RaiseTextChanged() End If End Set End Property End Class [C#] class Control: Component { string text. RaiseTextChanged(). } set { if (!text. Sin los eventos <Property>Changed y Raise<Property>Changed. Estos eventos indican al programador que el valor de una propiedad está cambiando o ha cambiado como resultado de una operación y no mediante una llamada al método en el objeto.Get Return m_text End Get Set If Not m_text. si el valor de una propiedad cambia debido a operaciones externas. si la base de datos cambia.Equals(value)) { text = value.

text = value. Se provoca un evento antes de que el valor de la propiedad cambie. } } } } . el valor de la propiedad cambia automáticamente.La propiedad Text de un control Edit es un ejemplo claro. } set { if (text != value) { OnTextChanging(Event. [Visual Basic] Class Edit Inherits Control Public Property Text() As String Get Return m_text End Get Set If m_text <> value Then OnTextChanging(Event. El nombre del evento es el nombre de la propiedad seguido del sufijo Changing. No se pasa ni el valor antiguo ni el nuevo. Mientras el usuario escribe información en el control.Empty).Empty) m_text = value End If End Set End Property End Class [C#] class Edit : Control { public string Text { get { return text. y el programador puede cancelar el evento iniciando una excepción. En el siguiente ejemplo de código se muestra un evento de cambio.

Empty) End If If Not (onPropertyChangedHandler Is Nothing) Then onPropertyChangedHandler(Me.Empty) m_text = value RaisePropertyChangedEvent(Edit. e) End If End Sub End Class [C#] class Edit : Control { public string Text { get { return text. [Visual Basic] Class Edit Inherits Control Public Property Text() As String Get Return m_text End Get Set If m_text <> value Then OnTextChanging(Event.PropertyChanged. También se debe provocar el evento PropertyChanged genérico.También se provoca un evento después de cambiar el valor de la propiedad. m_text) Then OnTextChanged(Event. El nombre del evento es el nombre de la propiedad seguido del sufijo Changed.Equals(Edit. } set { if (text != value) . En el siguiente ejemplo se muestra el uso del método OnPropertyChanged.ClassInfo. El modelo para provocar estos dos eventos es provocar el evento específico desde el método OnPropertyChanged. m_text) End If End Set End Property Protected Sub OnPropertyChanged(e As PropertyChangedEventArgs) If e. Este evento no se puede cancelar.ClassInfo.

e).{ OnTextChanging(Event.text).Empty).ClassInfo. } } } protected void OnPropertyChanged(PropertyChangedEventArgs e) { if (e.Equals(Edit.ClassInfo. debe examinar los mensajes de Windows para determinar cuando puede cambiar el texto. RaisePropertyChangedEvent(Edit. Para provocar el evento TextChanging. • Utilice una propiedad cuando el miembro sea un miembro de datos lógico. busque todos los lugares en los que el valor de la propiedad puede cambiar y proporcione la capacidad de cancelar el evento.PropertyChanged. En las siguientes declaraciones de miembro. el control Edit del ejemplo anterior no es del todo exacto porque el valor Text está almacenado en realidad en el identificador de ventana (HWND). Por ejemplo. Name es una propiedad porque es un miembro lógico de la clase. Al provocar el evento de cambio.text)) OnTextChanged(Event. En general. Propiedades frente a métodos Los diseñadores de bibliotecas de clases deben decidir a menudo entre implementar un miembro de la clase como una propiedad o como un método. } } En algunos casos el valor subyacente de una propiedad no se almacena como campo.Empty). los métodos representan acciones y las propiedades representan datos. if (onPropertyChangedHandler != null) onPropertyChangedHandler(this. y permitir que se inicie una excepción en OnTextChanging para cancelar el evento. text = value. esto complica el seguimiento de los cambios de valores. [Visual Basic] Public Property Name As String Get Return m_name End Get Set m_name = value . Si es muy difícil proporcionar un evento de cambio. se puede admitir sólo el evento cambiado. Utilice las instrucciones siguientes para decidir entre estas opciones.

conduce a un código ineficaz.Equals ("text")) . es necesario devolver una copia de la matriz interna para que el usuario no pueda cambiar el estado interno. i < type. desea ponerse en contacto con el usuario para que tenga en cuenta que debe almacenar el resultado en la caché.Methods[i]. } • Utilice un método cuando: • • • • • • • La operación es una conversión. El miembro es estático. Al llamar al miembro dos veces seguidas da lugar a resultados distintos.Methods(i). for (int i = 0. End If Next i [C#] Type type = // Get a type. i++) { if (type. } set { name = value. por tanto.Methods. Esto junto con el hecho de que el usuario puede creer fácilmente que es una propiedad indizada. se crearán 2 +1 copias de la matriz en el siguiente bucle. como Object. El miembro devuelve una matriz.ToString.Equals("text") Then ' Perform some operation.Length .Name.1 If type.End Set End Property [C#] public string Name get { return name. Como resultado. Obtiene el valor de una propiedad mediante el descriptor de acceso get y esta acción tiene efectos secundarios visibles. cada llamada a la propiedad Methods crea una copia de la n matriz. El orden de ejecución es importante.Length. Por lo general. La operación es bastante costosa y. En el siguiente ejemplo de código. pero devuelve un valor que se puede cambiar. [Visual Basic] Dim type As Type = ' Get a type. Tenga en cuenta que las propiedades de un tipo se deben poder establecer y recuperar en cualquier orden. Dim i As Integer For i = 0 To type.Methods.Name. Las propiedades que devuelven matrices pueden ser muy imprecisas.

[Visual Basic] Class Connection ' The following three members should be properties ' because they can be set in any order. End Property ' The following member should be a method ' because the order of execution is important. Property DNSName() As String ' Code for get and set accessors goes here. } .} string UserName {get{}. Function Execute() As Boolean [C#] class Connection { // The following three members should be properties // because they can be set in any order. // This method cannot be executed until after the // properties have been set. End Property Property UserName() As String ' Code for get and set accessors goes here.} // The following member should be a method // because the order of execution is important.set{}. bool Execute (). string DNSName {get{}. } } En el ejemplo siguiente se muestra el uso correcto de propiedades y métodos.} string Password {get{}.set{}.set{}. ' This method cannot be executed until after the ' properties have been set.{ // Perform some operation. End Property Property Password() As String 'Code for get and set accessors goes here.

La compatibilidad con indizadores del lenguaje de programación C# hace que se cumpla esta regla. Si no es así. vea la Propiedad DataGrid. se debe cambiar la propiedad Method al método GetMethod(string). ignoreCase As Boolean) As MethodInfo [C#] // Change the MethodInfo Type. No utilice propiedades de sólo escritura. No utilice propiedades indizadas no predeterminadas. Si el diseño requiere varios índices. [Visual Basic] • • • • • ' Change the MethodInfo Type. reconsidere si representa un miembro de datos lógico. MethodInfo Type. Siga esta regla. reconsidere si representa un miembro de datos lógico.GetMethod(name As String. los indizadores siempre adoptan el nombre Item.Method property is changed to ' the MethodInfo Type. a no ser que haya un nombre más obvio para los usuarios como la propiedad Chars de la clase String. En el siguiente ejemplo de código. En las reglas siguientes se describen las instrucciones de utilización de propiedades indizadas: • • Utilice una propiedad indizada cuando el miembro de datos lógico de la propiedad sea una matriz.Method(name As String) As MethodInfo Function Type. use un método.Method property to a method. Tenga en cuenta que esto no está permitido en C#.GetMethod method.Item. Utilice sólo una propiedad indizada por clase y convierta esta propiedad en el valor predeterminado de esa clase. Si el diseño requiere otros tipos para las propiedades indizadas. Uso de propiedades indizadas Nota Las propiedades indizadas también se conocen como indizadores. Boolean ignoreCase) [Visual Basic] ' The MethodInfo Type. En C#. Considere usar sólo un índice.Method property to a method. ignoreCase As Boolean) As MethodInfo [C#] . use un método. Considere la utilización sólo de valores enteros o cadenas para las propiedades indizadas. Property Type. Por ejemplo.GetMethod(name As String) As MethodInfo Function Type.Method[string name] MethodInfo Type. Asigne el nombre Item a una propiedad indizada. No proporcione una propiedad indizada y un método que sean equivalentes semánticamente a dos o más métodos sobrecargados. C# no lo permite.GetMethod (string name.GetMethod(name As String.Propiedades de sólo lectura y escritura Se debe utilizar una propiedad de sólo lectura cuando el usuario no puede cambiar el miembro de datos lógicos de la propiedad. Function Type. Si no es así.

Method property is changed to // the MethodInfo Type.GetMethod(string name) MethodInfo Type. Boolean ignoreCase) .// The MethodInfo Type. MethodInfo Type.GetMethod (string name.GetMethod method.

La finalidad de este método es proporcionar a una clase derivada una forma de controlar el evento mediante un reemplazo. public delegate void MouseEventHandler(object sender. "se provocó un evento". • • Cuando un evento contenga datos importantes. En los lenguajes que admiten la palabra clave void. e) End If End Sub End Class . [Visual Basic] Public Class MouseEventArgs Inherits EventArgs ' Code for the class goes here. El nombre del método se convierte en OnEventName. End Class [C#] public class MouseEvent: EventArgs {} • Utilice un método virtual protected (Protected en Visual Basic) para provocar cada evento. Cuando haga referencia a eventos en la documentación. Esta técnica no es adecuada para clases sealed porque no permiten derivar ninguna clase. MouseEventArgs e). Por ejemplo: [Visual Basic] Public Class Button Private onClickHandler As ButtonClickHandler Protected Overridable Sub OnClick(e As ClickEventArgs) ' Call the delegate if non-null. utilice el tipo de valor devuelto de void para los controladores de eventos. en vez de "se activó un evento" o "se desencadenó un evento". Esto es más natural que utilizar delegados en las situaciones en las que el programador crea una clase derivada. Las clases de eventos deben extender la Clase System. use clases de datos de eventos con establecimiento inflexible de tipos. utilice la frase. donde EventName es el nombre del evento provocado. If Not (onClickHandler Is Nothing) Then onClickHandler(Me. tales como las coordenadas de un clic del mouse (ratón). como se muestra en el siguiente código en lenguaje C# de ejemplo.Instrucciones de uso de eventos En las reglas siguientes se describen las instrucciones de uso de eventos: • • • Elija un nombre para el evento siguiendo las Instrucciones de nomenclatura de eventos recomendadas.EventArgs. como se muestra en el ejemplo siguiente.

protected virtual void OnClick(ClickEventArgs e) { // Call the delegate if non-null. no incluya ningún procesamiento en el método OnEventName que sea necesario para que la clase base funcione correctamente. } } La clase derivada puede elegir no llamar a la clase base durante el procesamiento de OnEventName. Como el programador puede realizar una función de devolución de llamada en el objeto para realizar otras acciones. el objeto debe tener el estado adecuado. If Not (windowHandle Is Nothing) Then ' Paint button in normal state. e). PaintDown() Try ' Call event handler.[C#] public class Button { ButtonClickHandler onClickHandler. Considere la utilización de un bloque try/finally en el punto del código donde se provoca el evento. PaintUp() End If End Try End Sub Protected Overridable Sub OnClick(e As ClickEvent) . if (onClickHandler != null) onClickHandler(this. Teniendo en cuenta esto. en todos los casos. Las clases deben estar preparadas para que el controlador de eventos realice cualquier operación y. OnClick() Finally ' Window might be deleted in event handler. no haga suposiciones sobre el estado del objeto cuando el control regrese al punto en el que provocó el evento. una vez provocado el evento. Por ejemplo: [Visual Basic] Public Class Button Private onClickHandler As ButtonClickHandler Protected Sub DoClick() ' Paint button in indented state. • Hay que tener en cuenta que un controlador de eventos puede contener cualquier código.

ComponentModel. e As NodeLabelEditEventArgs) . OnClick().If Not (onClickHandler Is Nothing) Then onClickHandler(Me.CancelEventArgs para permitir que el programador controle los eventos de un objeto. Por ejemplo. PaintDown(). } } protected virtual void OnClick(ClickEvent e) { } } • Utilice o extienda la Clase System. Public Class Form1 Inherits Form Private treeView1 As New TreeView() Sub treeView1_BeforeLabelEdit(source As Object. En el siguiente ejemplo de código se muestra cómo puede utilizar un programador este evento para evitar la edición de un nodo. e) End If End Sub End Class [C#] public class Button { ButtonClickHandler onClickHandler. } finally { // Window might be deleted in event handler. PaintUp(). e). el control TreeView provoca BeforeLabelEdit cuando el usuario se dispone a editar una etiqueta de nodo. if (windowHandle != null) // Paint button in normal state. [Visual Basic] if (onClickHandler != null) onClickHandler(this. protected void DoClick() { // Paint button in indented state. try { // Call event handler.

e.CancelEdit = True End Sub End Class
[C#]

public class Form1: Form { TreeView treeView1 = new TreeView(); void treeView1_BeforeLabelEdit(object source, NodeLabelEditEventArgs e) { e.CancelEdit = true; } } Observe que en este caso, no se genera un error. Se trata de una etiqueta de sólo lectura. Cancelar eventos no resulta adecuado en los casos en que el programador cancelaría la operación y devolvería una excepción. En estos casos, se debe provocar una excepción dentro del controlador de eventos para cancelarlos. Por ejemplo, supongamos que el usuario desea escribir lógica de validación en un control de edición como se muestra a continuación.
[Visual Basic]

Public Class Form1 Inherits Form Private edit1 As EditBox = New EditBox() Sub TextChanging(source As Object, e As EventArgs) Throw New RuntimeException("Invalid edit") End Sub End Class
[C#]

public class Form1: Form { EditBox edit1 = new EditBox(); void TextChanging(object source, EventArgs e) { throw new RuntimeException("Invalid edit"); }

Instrucciones de uso de métodos
En las reglas siguientes se describen las instrucciones de uso de métodos: • • • Elija un nombre para el evento siguiendo las Instrucciones de nomenclatura de eventos. No utilice la notación húngara. De forma predeterminada, los métodos no son virtuales. Mantenga esta característica predeterminada en las situaciones en las que no es necesario utilizar métodos virtuales. Para obtener más información sobre la herencia de implementación, vea Instrucciones de uso de clases base.

Instrucciones de sobrecarga de métodos La sobrecarga de métodos se produce cuando una clase contiene dos métodos con el mismo nombre, pero firmas diferentes. En esta sección se proporcionan algunas instrucciones de utilización de métodos sobrecargados. • • Utilice la sobrecarga de métodos para proporcionar métodos diferentes que semánticamente hagan lo mismo. Utilice la sobrecarga de métodos, en vez de permitir argumentos predeterminados. Los argumentos predeterminados no controlan bien las versiones y, por tanto, no se permiten en Common Language Specification (CLS). En el siguiente ejemplo de código se muestra un método String.IndexOf sobrecargado.
[Visual Basic]

Function String.IndexOf(name As String) As Integer Function String.IndexOf(name As String, startIndex As Integer) As Integer
[C#]

int String.IndexOf (String name); int String.IndexOf (String name, int startIndex); • Utilice los valores predeterminados correctamente. En una familia de métodos sobrecargados, el método complejo debe utilizar nombres de parámetros que indiquen un cambio del estado predeterminado que se supone en el método sencillo. Por ejemplo, en el código siguiente el primer método supone que la búsqueda no hará distinción entre mayúsculas y minúsculas. En el segundo método se utiliza el nombre ignoreCase en vez de caseSensitive para indicar cómo se cambia el comportamiento predeterminado.
[Visual Basic]

' Method #1: ignoreCase = false. Function Type.GetMethod(name As String) As MethodInfo ' Method #2: Indicates how the default 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 default behavior of method #1 is being // changed. MethodInfo Type.GetMethod (String name, Boolean ignoreCase); • Utilice un modelo de nomenclatura y de orden coherentes en los parámetros del método. Lo habitual es proporcionar un conjunto de métodos sobrecargados con un número creciente de parámetros para que el programador pueda especificar el nivel de información deseado. Cuantos más parámetros especifique, más detalles puede especificar el programador. En el siguiente ejemplo de código, el método Execute contiene un orden de parámetros coherente y una variación del modelo de nomenclatura. Cada variación del método Execute utiliza la misma semántica para el conjunto compartido de parámetros.
[Visual Basic]

Public Class SampleClass Private defaultForA As String = "default value for a" Private defaultForB As Integer = "42" Private defaultForC As Double = "68.90" Overloads Public Sub Execute() Execute(defaultForA, defaultForB, defaultForC) End Sub Overloads Public Sub Execute(a As String) Execute(a, defaultForB, defaultForC) End Sub Overloads Public Sub Execute(a As String, b As Integer) Execute(a, b, defaultForC) End Sub Overloads Public Sub Execute(a As String, b As Integer, c As Double) Console.WriteLine(a) Console.WriteLine(b) Console.WriteLine(c) Console.WriteLine() End Sub End Class
[C#]

public class SampleClass

defaultForC). En el ejemplo siguiente se muestra este modelo. } public void Execute (string a. establezca sólo la sobrecarga más completa como virtual y defina las otras operaciones en función de esta sobrecarga. startIndex As . defaultForC). b. Console. double c) { Console. Console. public void Execute() { Execute(defaultForA. defaultForB. Console. int b.WriteLine(b).WriteLine(a). • Si debe proporcionar la capacidad de reemplazar un método. int b) { Execute (a. } public void Execute (string a) { Execute(a. 0) End Function Overloads Public Function IndexOf(s As String.90". readonly double defaultForC = "68. [Visual Basic] Public Class SampleClass Private myString As String Public Sub New(str As String) Me. defaultForC).WriteLine(c).WriteLine(). } } Tenga en cuenta que el único método del grupo que debe ser virtual es el que más parámetros tiene y sólo cuando se precisa extensibilidad. } public void Execute (string a.myString = str End Sub Overloads Public Function IndexOf(s As String) As Integer Return IndexOf(s. readonly int defaultForB = "42". defaultForB.{ readonly string defaultForA = "default value for a".

} public int IndexOf(string s) { return IndexOf (s.myString = str.) ya que no es compatible con Common Language Specification.IndexOf(s.. startIndex. En un código sensible al rendimiento.startIndex ). int count) {return myString. count). myString.. utilice la palabra clave params (ParamArray en Visual Basic) para esta construcción. int startIndex. Un ejemplo clásico es el método printf del lenguaje de programación C.Length . startIndex As Integer. int startIndex) {return IndexOf(s. startIndex.startIndex) End Function Overloads Public Overridable Function IndexOf(s As String. count) End Function End Class [C#] public class SampleClass { private string myString. } public int IndexOf(string s. } } Métodos con un número variable de argumentos Puede que desee exponer un método que incluya un número variable de argumentos. Sólo se debe hacer esto para la ruta de acceso a un código . startIndex. } public virtual int IndexOf(string s. 0). startIndex. utilice el siguiente código en vez de varios métodos sobrecargados. ParamArray args() As Object) [C#] void Format(string formatString.Length . En las bibliotecas de clases administradas. public MyClass(string str) { this. count As Integer) As Integer Return myString. [Visual Basic] Sub Format(formatString As String. Por ejemplo. myString. puede proporcionar rutas de acceso de código especiales para un reducido número de elementos.Integer) As Integer Return IndexOf(s.IndexOf(s. params object [] args) No es conveniente utilizar únicamente la convención de llamada VarArgs o puntos suspensivos (.

[Visual Basic] Sub Format(formatString As String. ParamArray args() As Object) [C#] void Format(string formatString. se recomienda utilizar el siguiente modelo para que haya un equilibrio entre el rendimiento y el costo del código de un caso especial. object arg1. object arg2) void Format(string formatString. object arg1) void Format(string formatString. En estos casos. arg1 As Object) Sub Format(formatString As String. arg1 As Object.completo de un caso especial (no únicamente crear una matriz y llamar al método general). arg2 As Object) Sub Format(formatString As String. params object [] args) .

lo que puede dañar el código de cliente. End Sub End Class [C#] public sealed class Environment { // Private constructor prevents the class from being created. private Environment() { // Code for the constructor goes here. } } • Reduzca al mínimo la cantidad de trabajo realizado en el constructor. el constructor predeterminado implícito será eliminado. el constructor privado evita que se genere una clase. Por lo tanto. muchos lenguajes de programación (como C#) agregan implícitamente un constructor público predeterminado. Tenga en cuenta que muchos compiladores no admiten que struct tenga un constructor sin parámetros. aunque se trate de un constructor público predeterminado. el tiempo de ejecución inicializa todos los campos de struct con un valor cero. Esto retrasa el costo que suponen más operaciones hasta que el usuario utilice una función específica de la instancia. No proporcione constructores sin parámetros para los valores del tipo struct. En el siguiente ejemplo. Private Sub New() ' Code for the constructor goes here. Utilice los parámetros de los constructores como un método abreviado para establecer propiedades. Esto hace que la creación de campos estáticos y matrices sea más rápida. Los constructores no deberían hacer más que capturar el parámetro o parámetros de constructor. agrega un constructor protegido. Tenga en cuenta que si agrega un constructor no predeterminado a una clase en una compilación posterior de la versión. [Visual Basic] NotInheritable Public Class Environment ' Private constructor prevents the class from being created. Si no se puede crear un tipo. • • Proporcione un constructor protected (Protected en Visual Basic) que se pueda utilizar en los tipos de una clase derivada. No debe haber ninguna diferencia semántica entre un constructor vacío seguido • • . Proporcione un constructor para cada clase.Instrucciones de uso de constructores En las reglas siguientes se describen las instrucciones de uso de constructores: • Proporcione un constructor privado predeterminado cuando sólo haya propiedades y métodos estáticos en una clase. Si no especifica un constructor. lo mejor es especificar siempre de forma explícita el constructor. Si la clase es abstracta. Si no se incluye un constructor. use un constructor privado.

"b").B = "b". se muestra un modelo de orden y nomenclatura de parámetros coherente para todos los constructores de SampleClass.WriteLine("New()") End Sub . Los tres ejemplos de código siguiente son equivalentes: [Visual Basic] ' Example #1. Un modelo común de parámetros de constructores sirve para proporcionar un número de parámetros cada vez mayor que permita a los programadores especificar el nivel de información deseado. más detalles puede especificar el programador. SampleClass. Class SampleClass = new Class(). • Utilice un modelo de nomenclatura y de orden coherente en los parámetros de constructores. Dim SampleClass As New Class("a") SampleClass. // Example #2. SampleClass. "b") [C#] // Example #1.B = "b" ' Example #3. Dim SampleClass As New Class("a". Class SampleClass = new Class ("a".A = "a" SampleClass. defaultForC) Console. SampleClass. En el siguiente código de ejemplo.de descriptores de acceso set y un constructor con múltiples argumentos.B = "b" ' Example #2. Dim SampleClass As New Class() SampleClass. Cuantos más parámetros especifique. [Visual Basic] Public Class SampleClass Private Const defaultForA As String = "default value for a" Private Const defaultForB As String = "default value for b" Private Const defaultForC As String = "default value for c" Public Sub New() MyClass. // Example #3.A = "a".B = "b". Class SampleClass = new Class("a"). defaultForB.New(defaultForA.

a = a Me. defaultForB. string c) } Instrucciones de uso de campos En las reglas siguientes se describen las instrucciones de uso de campos: • No utilice campos de instancias que sean public o protected (Public o Protected en Visual Basic).c = c End Sub End Class [C#] public class SampleClass { private const string defaultForA = "default value for a". private const string defaultForB = "default value for b".Public Sub New(a As String) MyClass. Si evita exponer directamente los campos al programador. Considere la inclusión de los descriptores de acceso de las propiedades get y set en los campos. En el siguiente ejemplo de código se muestra el uso correcto de campos de instancia privados con los descriptores de acceso de las propiedades get y set. se pueden tener versiones de las clases más fácilmente ya que no se puede cambiar un campo en una propiedad y seguir manteniendo la compatibilidad binaria. defaultForC) {} public MyClass (string a. c As String) Me. defaultForB. b As String) MyClass. b As String. como crear un objeto a petición. string b. defaultForC) End Sub Public Sub New(a As String. defaultForC) {} public MyClass (string a. [Visual Basic] Public Structure Point . public MyClass():this(defaultForA. string b) : this(a. private const string defaultForC = "default value for c". defaultForB. en vez de hacerlos públicos. b. b. defaultForC) End Sub Public Sub New(a As String. La presencia de código ejecutable en los descriptores de acceso de las propiedades get y set. permite introducir mejoras más adelante.New(a.New(a. defaultForC) {} public MyClass (string a) : this(a. cuando se utiliza la propiedad o en la notificación de cambio de propiedad.b = b Me.

} public int X { get . this. y As Integer) Me. private int yValue.xValue = x Me. public Point(int x.yValue = y.Private xValue As Integer Private yValue As Integer Public Sub New(x As Integer.yValue = y End Sub Public Property X() As Integer Get Return xValue End Get Set xValue = value End Set End Property Public Property Y() As Integer Get Return yValue End Get Set yValue = value End Set End Property End Structure [C#] public struct Point { private int xValue.xValue = x. int y) { this.

} set { yValue = value. } } } • Use la palabra clave const (Const en Visual Basic) para declarar campos de constantes que no vayan a cambiar. } } public int Y { get { return yValue. Esto se muestra en el siguiente ejemplo de código: [Visual Basic] Public Class Control Inherits Component Private handle As Integer Protected ReadOnly Property Handle() As Integer Get Return handle End Get End Property End Class [C#] public class Control: Component { private int handle. protected int Handle { get { return handle. } } } • Exponga un campo a una clase derivada utilizando una propiedad protected que devuelve el valor del campo. .{ return xValue. } set { xValue = value. Los compiladores de lenguajes guardan los valores de los campos const directamente en código de llamada.

En el siguiente ejemplo de código se muestra el uso correcto de campos estáticos públicos de sólo lectura. g As Byte. Utilice el estilo de Mayúsculas y minúsculas Pascal pues son campos públicos. [Visual Basic] Public Structure Color Public 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 code here. End Sub Public ReadOnly Property RedValue() As Byte Get Return Color End Get End Property Public ReadOnly Property GreenValue() As Byte Get Return Color End Get End Property Public ReadOnly Property BlueValue() As Byte Get Return Color End Get End Property End Structure [C#] public struct Color { public static readonly Color Red = new Color(0x0000FF). b As Byte) ' Insert code here. Si ya hay instancias predefinidas de un objeto.• Utilice campos estáticos públicos de sólo lectura para instancias de objetos predefinidas. declárelas como campos estáticos públicos de sólo lectura del objeto. . End Sub Public Sub New(r As Byte.

Los nombres correctos describen la semántica y no el tipo. public static readonly Color White = new Color(0xFFFFFF). } } } • Escriba al completo todas las palabras utilizadas en un nombre de campo. Realice la validación de argumentos para cada método protegido o público y descriptor de accesos de la propiedad set. public static readonly Color Black = new Color(0x000000). Por ejemplo. public static readonly Color Blue = new Color(0xFF0000). } • • No utilice la notación húngara en nombres de campos. No utilice mayúsculas en los nombres de campos.} public byte RedValue { get { return Color. [Visual Basic] Class SampleClass Private url As String Private destinationUrl As String End Class [C#] class SampleClass { string url. } } public byte GreenValue { get { return Color. byte g. aplicar un prefijo g_ o s_ es incorrecto. no aplique un prefijo al nombre de un campo para diferenciar los campos estáticos de los campos no estáticos. byte b) { // Insert code here. string destinationUrl. Instrucciones de uso de parámetros En las reglas siguientes se describen las pautas de uso de parámetros: • Compruebe si los argumentos de los parámetros son válidos.public static readonly Color Green = new Color(0x00FF00). En concreto. Utilice abreviaturas sólo si los programadores en general las comprenden. se muestra un ejemplo de nombres de campos correctos. } } public byte BlueValue { get { return Color. No aplique un prefijo a nombres de campos o a nombres de campos estáticos.} public Color(byte r. Inicie excepciones significativas para el programador en los argumentos de parámetros no . A continuación. public Color(int rgb) { // Insert code here.

") End If ' Check for valid parameter. "End is invalid. En el siguiente ejemplo se comprueba si los argumentos de los parámetros son válidos y se inician excepciones significativas.válidos. Use la clase System. Console. "Value is invalid.") End If ' Insert code to do other work here. [end] As Integer) ' Check for valid parameter.WriteLine("Starting at {0}".WriteLine("Ending at {0}". start) Console.") End If countValue = value End Set End Property Public Sub SelectItem(start As Integer. If value < 0 Or value >= maxValue Then Throw New ArgumentOutOfRangeException("value". [end]) End Sub . [Visual Basic] Class SampleClass Private countValue As Integer Private maxValue As Integer = 100 Public Property Count() As Integer Get Return countValue End Get Set ' Check for valid parameter. start. value. "Start is invalid. If start < 0 Then Throw New ArgumentOutOfRangeException("start".ArgumentException.ArgumentException o una clase derivada de System. [end]. If [end] < start Then Throw New ArgumentOutOfRangeException("end".

end."start". public void Add(object value){} .start. Se puede producir en un nivel inferior de rutinas privadas.GetString( "InvalidArgument".GetString("InvalidArgument".ToString())). if (count < 0 || count >= MaxValue) throw newArgumentOutOfRangeException( Sys. La cuestión principal es que en todo el área de la superficie que se expone al programador se comprueba si los argumentos son válidos. if (end < start) throw new ArgumentException( Sys.ToString())).End Class [C#] class SampleClass { public int Count { get { return count. • Asegúrese de comprender totalmente las implicaciones de pasar parámetros por valor o por referencia. } set { // Check for valid parameter. // Check for valid parameter. Pasar un parámetro por valor copia el valor que se está pasando y no produce ningún efecto en el valor original. } } public void Select(int start. if (start < 0) throw new ArgumentException( Sys. En el siguiente ejemplo de método se pasan parámetros por valor. int end) { // Check for valid parameter.GetString("InvalidArgument".count.ToString())). } } Tenga en cuenta que la comprobación real no tiene que producirse necesariamente en los métodos public o protected."value"."end".

se pueden realizar cambios en el valor del parámetro. En el siguiente ejemplo de método se pasa un parámetro por referencia.dll"] public static extern bool QueryPerformanceCounter(out long value) . En el siguiente ejemplo de método se pasa un parámetro de salida.Pasar un parámetro por referencia pasa la ubicación de almacenamiento del valor. sólo se pueden realizar cambios en el parámetro de salida. Como resultado. int value){} Un parámetro de salida representa la misma ubicación de almacenamiento que la variable especificada como argumento en la invocación del método. public static int Exchange(ref int location. [DllImport("Kernel32. Como resultado.

algunas son controles de interfaz de usuario (UI) y otras son servicios Web XML). Debe agregar extensibilidad o polimorfismo al diseño sólo si tiene un escenario del cliente claro. los programadores deberán programar específicamente cada adaptador. Una clase sealed no admite una clase derivada. es muy importante proporcionar un modelo coherente. Es conveniente utilizar clases. Por ejemplo. Por lo tanto. Si no se desea que ésta se pueda crear. Como las interfaces no son compatibles con la herencia de implementación. el beneficio que proporciona una interfaz es mínimo. proporcionar una interfaz para adaptadores de datos es complicado y no ofrece beneficios reales. los compiladores agregan un constructor público predeterminado a las clases que no definen un constructor. Sin embargo. se puede configurar el constructor como privado. a continuación. Siga estas instrucciones para crear clases base. las clases son más flexibles que las interfaces. no es necesario que sean coherentes entre todos los adaptadores. . Se pueden proporcionar modelos coherentes para programadores en las clases base. Clases base e interfaces Un tipo de interfaz es una especificación de un protocolo. el modelo aplicado a las clases no se aplica a las interfaces. Se debería proporcionar un constructor de forma explícita para todas las clases. lo mejor es definir siempre al menos un constructor por clase. potencialmente compatible con muchos tipos de objetos. La utilización de interfaces es adecuada en las situaciones siguientes: • • Hay varias clases no relacionadas que son compatibles con el protocolo. Utilice clases base en vez de interfaces siempre que sea posible. Aunque una interfaz o clase abstracta no es lo apropiado en esta situación. por tanto. cualquier clase que implemente la interfaz interrumpirá la ejecución porque la clase no implementa el nuevo método. Para obtener una descripción detallada de la lista completa de tipos de datos compatibles con el motor de tiempo de ejecución. en vez de otros tipos. Si la intención es que la clase no se pueda crear. el hecho de que contenga dicho constructor público predeterminado puede resultar confuso para los usuarios de ésta. normalmente. Agregar un método a una interfaz es lo mismo que agregar un método abstracto a una clase base. Desde la perspectiva de las versiones. se puede incluir la versión 1. Las clases base pueden proporcionar un conjunto predeterminado de funcionalidades a la vez que permiten la personalización mediante la ampliación. Estas clases ya han establecido clases base (por ejemplo.0 agregar un nuevo método a la clase. Además.0 y.Instrucciones de uso de tipos Los tipos constituyen las unidades de encapsulación en Common Language Runtime. En esta sección se proporcionan algunas instrucciones de utilización de clases básicas de tipos. vea Sistema de tipos común. Siempre que el método no sea abstracto. las clases derivadas existentes seguirán funcionando sin sufrir ningún cambio. Las clases base son una forma útil de agrupar objetos que comparten un conjunto común de funcionalidades. Instrucciones de uso de clases base Una clase es uno de los tipos más común. Con una clase. Una clase abstracta requiere una clase derivada para proporcionar una implementación. Las clases pueden ser abstractas o selladas. ya que. en la versión 2.

Me.width = width Me. Sin embargo. de este modo.specified = specified Console. Este método se debe marcar con el sufijo Impl. La interfaz pública de una clase base debe proporcionar un conjunto completo de funcionalidades para el consumidor de la clase. Me. specified) End Sub Protected Overridable Sub SetBoundsImpl(x As Integer. En el resto de las situaciones. Me. width As Integer. los usuarios de la clase a menudo desean implementar el menor número de métodos posible y. y As Integer. bounds {4}". y. specified As BoundsSpecified) SetBoundsImpl(x. height As Integer. En el siguiente ejemplo de código se muestra este proceso.y. y As Integer.width. width As Integer. y.x = x Me. width As Integer. width. Me. y {1}. La utilización de este modelo también proporciona lo que se conoce como método Template. height. height {3}.y = y Me.height.• La agregación no es adecuada o no es viable. Con esta finalidad. Me. proporcione un conjunto de métodos públicos finales o no virtuales que llaman a través de un único método protegido que proporciona implementaciones para los métodos.height = height Me. height. specified As BoundsSpecified) ' Insert code to perform meaningful operations here. Constructores y métodos protegidos Proporcione la personalización de clases a través de métodos protegidos. Me.specified) . proporcionar el conjunto completo de funcionalidades al consumidor. width. height As Integer) SetBoundsImpl(x. es mejor utilizar el modelo de herencia de clases. height As Integer. width {2}. y As Integer. Me.x.WriteLine("x {0}.specified) End Sub Overloads Public Sub SetBounds(x As Integer. [Visual Basic] Public Class SampleClass Private x As Integer Private y As Integer Private width As Integer Private height As Integer Private specified As BoundsSpecified Overloads Public Sub SetBounds(x As Integer.

public void SetBounds(int x. private int height.specified). Instrucciones de uso de clases sealed Utilice clases sealed si sólo hay propiedades y métodos estáticos en una clase. width. int width.height = height. BoundsSpecified specified) { SetBoundsImpl(x. deberá definir explícitamente un constructor protected en todas las clases abstractas. int y.y = y. this. this.width = width.specified = specified. Para obtener una descripción de todos los tipos de datos integrados de . int y. como el de C#. this. int width. vea . Por tanto. int height. int height) { SetBoundsImpl(x. specified). width. private int width. insertarán uno. BoundsSpecified specified) { // Add code to perform meaningful opertions here. int width. height. int y. la mayoría de los compiladores. this. this. y.End Sub End Class [C#] public class MyClass { private int x. BoundsSpecified specified. Instrucciones de uso de tipos de valor Un tipo de valor describe un valor que se representa como una secuencia de bits almacenada en la pila. private int y. } public void SetBounds(int x. } protected virtual void SetBoundsImpl(int x. } } Si no incluye un constructor public o protected. para una mejor documentación y legibilidad del código fuente.x = x. y. int height. this. height.NET Framework.

En esta sección se proporcionan las instrucciones de uso de tipos de valor de enumeraciones (enum) y de estructuras (struct). [Visual Basic] Public Structure Int32 Implements IFormattable Implements IComparable Public Const MinValue As Integer = -2147483648 Public Const MaxValue As Integer = 214748364 Private intValue As Intege Overloads Public Shared Function ToString(i As Integer) As String ' Insert code here. End Function Overloads Public Function ToString(ByVal format As String. Tienen un tamaño de instancia inferior a 16 bytes. Es conveniente utilizar la semántica de valor. Son inmutables. ByVal formatProvider As IFormatProvider) As String Implements IFormattable. End Function Public Shared Function Parse(s As String) As Integer ' Insert code here.Tipos de valor. End Function Overloads Public Overrides Function ToString() As String ' Insert code here.ToString ' Insert code here. Return 0 End Function Public Overrides Function GetHashCode() As Integer ' Insert code here. Return 0 End Function . En el siguiente ejemplo se muestra una estructura definida correctamente. Instrucciones de uso de estructuras Se recomienda utilizar un valor struct para los tipos que cumplan los siguientes criterios: • • • • Actúan como tipos primitivos.

IFormatProvider formatProvider) { // Insert code here. } public string ToString(string format. return 0. Return False End Function Public Function CompareTo(obj As Object) As Integer Implements IComparable. return false. } public override bool Equals(object obj) { // Insert code here. public static string ToString(int i) { // Insert code here. Return 0 End Function End Structure [C#] public struct Int32: IComparable. } public override string ToString() { // Insert code here. return 0. public const int MaxValue = 2147483647. } public override int GetHashCode() { // Insert code here.Public Overrides Overloads Function Equals(obj As Object) As Boolean ' Insert code here. } .CompareTo ' Insert code here. IFormattable { public const int MinValue = -2147483648. } public static int Parse(string s) { // Insert code here.

propiedades seguros. return 0. De este modo. } } • No incluya un constructor predeterminado para struct. Defina siempre los valores enumerados que se utilizan en parámetros y propiedades mediante el tipo enum. Las instancias de estructuras se pueden crear con un valor de cero sin ejecutar un constructor. No haga que una estructura struct dependa de un constructor que se invoque para cada instancia. las herramientas de desarrollo conocen los valores posibles de una propiedad o parámetro. El motor en tiempo de ejecución inserta un constructor que inicializa todos los valores en un estado cero. false o null (según corresponda) para que sean válidos. También se debe diseñar una estructura struct para un estado en el que todos los datos de instancias se establezcan en cero. Utilice un tipo de valor enum para escribir tipos devueltos. Esto permite crear matrices de estructuras sin ejecutar el constructor en cada instancia. parámetros. Instrucciones de uso de enumeraciones En las reglas siguientes se describen las pautas de uso de enumeraciones: • • No utilice el sufijo Enum en tipos enum. Tenga en cuenta que C# no admite que struct tenga un constructor predeterminado.public int CompareTo(object obj) { // Insert code here. [Visual Basic] Public Enum FileMode Append Create CreateNew Open OpenOrCreate Truncate End Enum . En el ejemplo siguiente se muestra cómo definir un tipo enum.

ByVal mode As FileMode). Truncate } En el ejemplo siguiente se muestra el constructor de un objeto FileStream que utiliza la enumeración FileMode.[C#] public enum FileMode { Append. [Visual Basic] <Flags()> Public Enum WatcherChangeTypes Created = 1 Deleted = 2 Changed = 4 Renamed = 8 All = Created Or Deleted Or Changed Or Renamed End Enum [C#] [Flags()] public enum WatcherChangeTypes { Created = 1. Deleted = 2. CreateNew. [Visual Basic] Public Sub New(ByVal path As String. • • • Utilice un tipo de valor enum en vez de constantes estáticas finales. [C#] public FileStream(string path. Changed = 4. OpenOrCreate. Utilice la Clase System. No utilice un tipo de valor enum en conjuntos abiertos (como la versión del sistema operativo).FlagsAttribute para crear atributos personalizados para un tipo de valor enum sólo si se realiza una operación OR bit a bit en valores numéricos. Create. Use potencias de dos para los valores de enum de forma que puedan combinarse con facilidad. . FileMode mode). Open. Este atributo se aplica en el siguiente ejemplo de código. Renamed = 8.

Se pueden dejar estas definiciones con las mayúsculas y minúsculas de Win32 que. Nota Una excepción a esta regla es la encapsulación de una API de Win32. [Visual Basic] Public Sub SetColor(newColor As Color) If Not [Enum]. Esto se muestra en el siguiente ejemplo de enumeración: [Visual Basic] <Flags()> _ Public Enum FileAccess Read = 1 Write = 2 ReadWrite = Read Or Write End Enum [C#] [Flags()] public enum FileAccess { Read = 1.IsDefined(GetType(Color). Tenga en cuenta que los argumentos enum no siempre se encuentran en el intervalo definido. Realice una validación de argumentos cómo se muestra en el siguiente ejemplo de código. newColor) Then Throw New ArgumentOutOfRangeException() End If . El tipo debe ser diferente de int para que sea compatible con versiones anteriores. Usar una operación OR bit a bit es un concepto avanzado y no debería requerirse para tareas sencillas. están todas las letras en mayúsculas. ReadWrite = Read | Write. • Considere proporcionar constantes con nombre para las combinaciones de indicadores usadas con frecuencia. Normalmente. se utilizan definiciones internas que vienen en un encabezado Win32. Resulta válido convertir cualquier valor entero al tipo enum aunque el valor no esté definido en el tipo enum. } • Utilice el tipo Int32 como el tipo subyacente de un tipo de valor enum salvo que se cumpla una de las siguiente condiciones: • • • El tipo de valor enum representa indicadores y hay ya más de 32 indicadores. Write = 2. o el tipo de valor enum podría tener muchos indicadores en el futuro. por lo general.All = Created | Deleted | Changed | Renamed }.

[Visual Basic] Public Class ObsoleteAttribute{} [C#] public class ObsoleteAttribute{} . que se puede colocar en elementos de programa como clases y métodos. que pueden tener parámetros con nombres y parámetros posicionales.IsDefined (typeof(Color). Notificaciones de eventos Utilice el modelo de diseño de eventos adecuado aunque el evento no esté relacionado con la interfaz de usuario. Instrucciones de uso de atributos . Estos métodos deben utilizar las convenciones de las funciones de devolución de llamada descritas en la sección Uso de las funciones de devolución de llamada.End Sub [C#] public void SetColor (Color color) { if (!Enum. en un marco de trabajo se puede definir un atributo HelpAttribute. especificar información declarativa para entidades de programas diversos y recuperar la información sobre atributos en el entorno del motor de tiempo de ejecución. Por ejemplo. para proporcionar una asignación de los elementos del programa a la documentación correspondiente.NET Framework permite a los programadores inventar nuevas clases de información declarativa. vea Instrucciones de uso de eventos. Pasar una función de devolución de llamada Compare a una rutina de ordenación es un ejemplo clásico de utilización de estas funciones. Termine los nombres de funciones de devolución de llamada con el sufijo Callback. Para obtener más información. vea Escribir atributos personalizados. } Instrucciones de uso de delegados Los delegados son una herramienta eficaz que permite al diseñador de modelos de objetos de código administrado encapsular llamadas a métodos. En la declaración de clases de atributos se pueden definir nuevas clases de información declarativa. color) throw new ArgumentOutOfRangeException(). Los delegados son muy útiles en las notificaciones de eventos y en las funciones de devolución de llamada. En las reglas siguientes se describen las instrucciones de uso de las clases de atributos: • Agregue el sufijo Attribute a clases de atributos personalizados. como se muestra en el siguiente ejemplo. Funciones de devolución de llamada Las funciones de devolución de llamada se pasan a un método para que se pueda llamar al código de usuario varias veces durante la ejecución para proporcionar personalización. Para obtener más información sobre la utilización de eventos.

End Sub Public ReadOnly Property UserName() As String Get Return UserName End Get End Property ' This is a named argument. End Class [C#] [AttributeUsage(AttributeTargets. En el ejemplo siguiente se muestra este modelo. [Visual Basic] • • Public Class NameAttribute Inherits Attribute ' This is a positional argument. pero cambie el uso de las mayúsculas y minúsculas para diferenciarlos. para que no se pueda derivar ninguna clase. [Visual Basic] <AttributeUsage(AttributeTargets. como se muestra en el siguiente ejemplo. Public Property Age() As Integer Get Return Age . Esto permite el acceso al argumento en tiempo de ejecución. Public Sub New(username As String) ' Implement code here. Inherited := False. Utilice argumentos con nombres (propiedades de lectura y escritura) en los parámetros opcionales. pero cambie el uso de las mayúsculas y minúsculas para diferenciarlos. Utilice argumentos posicionales (parámetros del constructor) en los parámetros requeridos. Proporcione una propiedad de lectura/escritura con el mismo nombre que el argumento con nombre.• Especifique AttributeUsage en los atributos para definir su utilización exacta.All. Proporcione una propiedad de sólo lectura con el mismo nombre que el argumento posicional. No defina un parámetro con argumentos posicionales y argumentos con nombre. AllowMultiple := True)> _ Public Class ObsoleteAttribute Inherits Attribute ' Insert code here.All. AllowMultiple = true)] public class ObsoleteAttribute: Attribute {} • • Selle las clases de atributos siempre que sea posible. Inherited = false.

ListBox.SelectedObjectCollection ' Without nested types. } public string UserName { get { return UserName. Utilícelos sólo en situaciones en las que se cumplan las condiciones siguientes: • El tipo anidado (tipo interno) pertenece de forma lógica al tipo contenedor (tipo externo). como un enumerador en una colección. public int Age { get { return Age. } } } Instrucciones de uso de tipos anidados Los tipos anidados son tipos definidos dentro del ámbito de otro tipo. ya que pueden tener acceso al estado privado. En los ejemplos siguientes se muestra cómo definir tipos con y sin tipos anidados: [Visual Basic] ' With nested types. Los tipos anidados resultan muy útiles para encapsular los detalles de implementación de un tipo. } } // This is a named argument. } set { Age = value. ListBoxSelectedObjectCollection . public NameAttribute (string username) { // Implement code here. Los tipos anidados públicos se deben utilizar en contadas ocasiones.End Get Set Age = value End Set End Property End Class [C#] public class NameAttribute: Attribute { // This is a positional argument.

sin embargo. Esta compatibilidad es la clave para desplazar una parte del código no administrado al código administrado cada vez. Un componente COM se puede utilizar dentro de un tipo administrado y una instancia administrada se puede utilizar en un componente COM. por tanto. usar y destruir sin usar el tipo externo. como alojarlos en un contenedor no administrado.ScrollBars ' Without nested types. Los tipos y las interfaces que se deben utilizar directamente en los clientes COM. . se deben marcar con el atributo ComVisible(true). esto indicaría que el tipo ocupa un lugar en su propia biblioteca. Las referencias al tipo se declaran normalmente en el código de cliente.ScrollBars // Without nested types. no debería estar anidado. por lo tanto. Para exponer totalmente un tipo administrado a clientes COM. las restricciones en la utilización de tipos administrados. esto reduce la exposición a COM y. el tipo debe exponer funcionalidad de forma que sea compatible y respete los términos del contrato de control de versiones de COM. Un tipo interno no se debería reutilizar ampliamente fuera del tipo externo sin una relación con el tipo externo. Se puede crear. ListBoxSelectedObjectCollection // With nested types. • Instrucciones para exponer funcionalidad a COM Common Language Runtime proporciona compatibilidad total para interoperar con componentes COM. RichTextBoxScrollBars [C#] // With nested types.SelectedObjectCollection // Without nested types.' With nested types. Si un tipo tiene un constructor público. RichTextBox. Nota Los miembros de un tipo también deben marcarse como ComVisible(false). El cierre transitivo de todos los tipos a los que se hace referencia en los tipos expuestos debe marcarse explícitamente como ComVisible(true). ListBox. RichTextBox. en caso contrario. RichTextBoxScrollBars No utilice tipos anidados si se cumplen las siguientes condiciones: • Las instancias del tipo deben crearse mediante el código de cliente. La razón tras esta directriz es que si se pueden crear instancias de un tipo anidado. se expondrán como IUnknown. Marque las bibliotecas de clases administradas con el atributo ComVisibleAttribute para indicar si los clientes COM pueden utilizar la biblioteca directamente o deben utilizar un contenedor para adaptar las funcionalidades. también presenta algunos problemas para los diseñadores de bibliotecas de clases. probablemente no debería anidarse.

las referencias de instancias se deben calcular con objetos de valores. System. Cálculo por referencia Los objetos de cálculo por referencia son Objetos utilizables de forma remota. Asegúrese de que comprende el impacto que tiene en el Registro hacer que todos los tipos se puedan crear conjuntamente.IO.Component. Si el tipo implementa la Interfaz IDisposable es muy probable que las referencias se calculen por referencia. En la mayoría de las secuencias. Estos tipos se deben marcar con el atributo Serializable.MarshalByRefObject. porque la clase base común o Clase System. Tipos cuyas referencias no se calculan en ningún caso a través de dominios AppDomains. En concreto. se encapsulan recursos externos para que las referencias se calculen con objetos de referencia. Esto significa que sus tipos se deben marcar como Serializable.Stream se deriva de MarshalByRefObject. Éste es el valor predeterminado. las referencias del recurso se deben calcular con un objeto de referencia. Estos tipos se deben derivar en última instancia de la Clase System. Éste ya debería ser el caso de la mayoría de componentes. Las referencias de tipos de componentes se deben calcular con objetos de referencia. Tipos para los que el motor de ejecución crea un proxy transparente cuando se calculan sus referencias a través de un límite AppDomain (en el mismo equipo o en un equipo diferente).Los tipos marcados con el atributo ComVisible(true) no pueden exponer las funcionalidades únicamente de forma que no se puedan utilizar en COM. Los tipos especiales que no se pueden llamar a través de un límite AppDomain (como el propietario de métodos estáticos de utilidades) no se deben marcar como Serializable. es una clase de cálculo por referencia. Si el tipo encapsula un recurso del sistema operativo. La interacción remota de objetos se aplica a las tres clases de tipos siguientes: • Tipos cuyas instancias se copian cuando se calculan sus referencias a través de un límite AppDomain (en el mismo equipo o en un equipo diferente). Pruebe la funcionalidad del tipo en clientes COM para comprobar que su comportamiento es correcto. En las instancias que simplemente alojan un estado las referencias se deben calcular con objetos de valor (como DataSet). • • • . COM no es compatible con los métodos estáticos ni con los constructores parametrizados. como FileStreams y NetworkStreams. • • Siga estas instrucciones al utilizar el cálculo de referencias por referencia: • • De forma predeterminada.

End Sub Public Sub New(message As String. se puede invocar File. use los constructores comunes que se muestran en el siguiente ejemplo de código.Instrucciones para provocar y controlar errores En las reglas siguientes se describen las instrucciones para provocar y controlar errores: • Todas las rutas de código que generan una excepción deben proporcionar un método para comprobar el funcionamiento correcto sin que se inicie una excepción. Termine los nombres de la clase Exception con el sufijo Exception como en el siguiente ejemplo. End Sub End Class [C#] . context As StreamingContext) ' Implementation code goes here. End Class [C#] public class FileNotFoundException : Exception { // Implementation code goes here. aunque esto puede no ser siempre posible. End Sub Public Sub New(info As SerializationInfo. [Visual Basic] • Public Class FileNotFoundException Inherits Exception ' Implementation code goes here.} • Al crear clases de excepciones. Por ejemplo. pero el objetivo consiste en que no se inicien excepciones en condiciones de ejecución normales. End Sub Public Sub New(message As String) ' Implementation code goes here. [Visual Basic] Public Class XxxException Inherits ApplicationException Public Sub New() ' Implementation code goes here. inner As Exception) ' Implementation code goes here.Exists. para evitar una excepción FileNotFoundException.

Datos como las rutas de acceso del sistema de archivos local se consideran información privilegiada. lo cual tendría un impacto negativo en el rendimiento y en el mantenimiento. un comando File. Incluya información adicional (además de la cadena de descripción) en una excepción sólo cuando haya un escenario mediante programación en el que la información adicional es útil. Código malicioso podría usar esta información para recopilar información de usuario privada del equipo. se derivará de la cadena de descripción de la excepción iniciada y nunca de la clase exception... Agrupe las nuevas excepciones derivadas de la clase base SystemException o ApplicationException por espacio de nombres. Utilice una cadena de descripción localizada en cada excepción..IO se agrupan bajo IOException (derivada de SystemException) y todas las excepciones Microsoft.public class XxxException : ApplicationException { public XxxException() {. • No derive todas las excepciones nuevas directamente de la clase base SystemException. Por ejemplo. Genere mensajes de error gramaticalmente correctos y con puntuación. pero inicia una excepción si el archivo está bloqueado.Media se podrían agrupar bajo MediaException (derivada de ApplicationException). Cada frase de la cadena de descripción de una excepción debe terminar con un punto. Exception inner) {. StreamingContext context) {. No obstante... Defina nuevos tipos de excepciones sólo para escenarios en los que espera que los usuarios de la biblioteca de clases detecten las excepciones de este nuevo tipo y realicen una acción mediante programación en función del tipo de excepción. o en el flujo normal de control. Proporcione las propiedades de la excepción para el acceso mediante programación.Open devuelve una referencia null si no encuentra el archivo. no tiene que tratar el caso en el que un programador se olvida de incluir un punto final. utilice los tipos de excepción predefinidos. Por ejemplo. herede sólo de SystemException. No utilice excepciones en errores normales o esperados... } public XxxException(string message) {. herede de ApplicationException. todas las excepciones System. Cuando el usuario vea un mensaje de error. Serán contadas las ocasiones en las que necesitará incluir información adicional en una excepción. Cuando cree nuevas excepciones en espacios de nombres System. No exponga información protegida en mensajes de excepciones. El código que normalmente muestra un mensaje de excepción al usuario. Esto es en vez de analizar la cadena de la excepción. Cuando cree nuevas excepciones en otros espacios de nombres. Debe devolver el valor null en los casos de error sumamente habituales. } public XxxException(string message. • • • • • • • . no es habitual administrar una excepción FileIOException en el código.} } • En la mayoría de los casos. } public XxxException(SerializationInfo info. tiene más sentido definir una excepción FileNotFoundException porque puede ocurrir que el programador decida crear el archivo que falta. Por ejemplo..

Tenga en cuenta que el seguimiento de la pila comienza en el punto de inicio de una excepción. En el siguiente ejemplo de código.ReadByte() <> true ' Do something.Open). // ReadByte returns -1 at end of file. } } • • • Inicie la excepción InvalidOperationException si la llamada a un método o a un descriptor de acceso set no es apropiada dado el estado actual del objeto. FileMode. [C#] } • class File . Utilice los métodos de generador de excepciones. End While End Sub End Class [C#] class FileRead { void Open() { FileStream stream = File. una clase FileStream expone otra forma de determinar si se ha llegado al final del archivo para evitar que se inicie una excepción si el programador lee más allá del final del archivo. Para evitar el código repetitivo. utilice los métodos auxiliares que crean la excepción y la devuelven utilizando el operador new.Open) Dim b As Byte ' ReadByte returns -1 at end of file.txt".Open("myfile. [Visual Basic] Class FileRead Sub Open() Dim stream As FileStream = File.• Diseñe las clases de forma que durante su uso normal nunca se inicie una excepción. while ((b = stream.Open("myfile. FileMode. byte b. y no en el punto en el que se haya creado con el operador new. Inicie una excepción ArgumentException o genere una excepción derivada de esta clase si se pasan o se detectan parámetros no válidos.ReadByte()) != true) { // Do something. Es normal que una clase inicie la misma excepción desde distintos lugares de su implementación. Considere esto cuando decida donde iniciar una excepción.txt". While b = stream. En el siguiente ejemplo de código se muestra cómo implementar un método auxiliar.

{ string fileName; public byte[] Read(int bytes) { if (!ReadFile(handle, bytes)) throw NewFileIOException(); } FileException NewFileIOException() { string description = // Build localized string, include fileName. return new FileException(description); } } • • • • • • • Inicie excepciones en lugar de devolver un código de error o HRESULT. Inicie la excepción más específica posible. Genere un texto de mensaje descriptivo de las excepciones para el programador. Establezca todos los campos en la excepción que utilice . Utilice excepciones internas (excepciones encadenadas). Sin embargo, no detecte ni reinicie las excepciones a menos que agregue más información o cambie el tipo de excepción. No cree métodos que inicien NullReferenceException o IndexOutOfRangeException. Realice la comprobación de argumentos en miembros protegidos (familia) y en miembros internos (ensamblado). Indique claramente en la documentación si el método protegido no realiza una comprobación de argumentos. A menos que se indique lo contrario, suponga que se realiza la comprobación de argumentos. No obstante, si no se realiza esta comprobación puede que el rendimiento mejore. Limpie cualquier efecto secundario producido al iniciar una excepción. Los llamadores deben suponer 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 puede asumir que el elemento especificado no se ha agregado al método Hashtable.

Tipos de excepciones estándar En la tabla siguiente se incluyen las excepciones estándar que proporciona el tiempo de ejecución y las condiciones para las que deberá crear una clase derivada. Tipo de excepción Exception Tipo base Object Descripción Ejemplo

Clase base de Ninguno (utilice todas las una clase excepciones. derivada de esta excepción).

SystemException

Exception

Clase base de todos los errores que genera el motor de tiempo de ejecución. La inicia el motor de tiempo de ejecución sólo cuando no se indiza correctamente una matriz.

Ninguno (utilice una clase derivada de esta excepción).

IndexOutOfRangeException

SystemException

Indizar una matriz fuera del intervalo válido: arr[arr.Length+1 ]

NullReferenceException

SystemException

La inicia el object o = null; motor de o.ToString(); tiempo de ejecución sólo cuando se hace referencia a un objeto nulo. La inician los métodos que se encuentran en un estado no válido. Llamar a Enumerator.Get Next() después de eliminar un Item de la colección subyacente.

InvalidOperationException

SystemException

ArgumentException

SystemException

Clase base de Ninguno (utilice todas las una clase excepciones de derivada de argumento. esta excepción). La inician los String s = null; métodos que "Calculate".Inde no permiten xOf (s); que un argumento sea nulo. s La inician String métodos que "string"; comprueban s.Chars[9]; que los argumentos están en un intervalo dado. =

ArgumentNullException

ArgumentException

ArgumentOutOfRangeException

ArgumentException

ExternalException

SystemException

Clase base para excepciones que se generan o que tienen como destino entornos fuera del motor de ejecución.

Ninguno (utilice una clase derivada de esta excepción).

COMException

ExternalException

Excepción que Se usa en la encapsula la interoperabilida información d COM. Hresult de COM. Excepción que encapsula la información de control estructurado de excepciones Win32. Se utiliza en la interoperabilida d de código no administrado.

SEHException

ExternalException

Excepciones de contenedor Los errores que se generan en el mismo nivel que un componente deben iniciar una excepción descriptiva para los usuarios de destino. En el siguiente ejemplo de código, el mensaje de error está destinado a los usuarios de la clase TextReader que intenten leer los datos de una secuencia.
[Visual Basic]

Public Class TextReader Public Function ReadLine() As String Try ' Read a line from the stream. Catch e As Exception Throw New IOException("Could not read from stream", e) End Try End Function End Class
[C#]

public class TextReader { public string ReadLine() { try { // Read a line from the stream. }

e).catch (Exception e) { throw new IOException ("Could not read from stream". } } } .

se debería usar una colección cuando se pueden utilizar métodos como Add. Aunque estos tipos tienen modelos de uso similares. Matrices y Colecciones En algunas ocasiones. los diseñadores de bibliotecas de clases tienen que decidir entre utilizar una matriz o devolver una colección. vea Agrupar datos en colecciones. Usar matrices No se debe devolver una instancia interna de una matriz. En general.VisualBasic Public Class ExampleClass NotInheritable Public Class Path Private Sub New() End Sub Private Property Path Get End Get Set End Set End Property Private Shared badChars() As Char = {Chr(34).Collections Imports Microsoft. El ejemplo siguiente muestra cómo la matriz badChars puede ser modificada por cualquier código que tenga acceso a la propiedad Path aunque la propiedad no implemente el descriptor de acceso set."<"c. ya que esto permite al código llamador cambiar la matriz.Array (Clase). Remove u otros para manipularla.">"c} Public Shared Function GetInvalidPathChars() As Char() Return badChars End Function End Class Public Shared Sub Main() . vea Matrices y System. las características de rendimiento son diferentes. [Visual Basic] Imports System Imports System. Para obtener más información sobre la utilización de colecciones.Instrucciones de uso de matrices Para obtener una descripción general de matrices y del uso de matrices.

For Each c In Path.' The following code displays the elements of the ' array as expected.GetInvalidPathChars()) { Console. } } .GetInvalidPathChars() Console.WriteLine(). Note that the values have changed.Write(c) Next c End Sub End Class [C#] using System. Console. foreach(char c in Path.Write(c) Next c Console. '<'. } public static void Main() { // The following code displays the elements of the // array as expected.GetInvalidPathChars()(0) = "A"c Path. Dim c As Char For Each c In Path.Collections. using System. public static char[] GetInvalidPathChars() { return badChars.WriteLine() ' The following code sets all the values to A. Path.GetInvalidPathChars() Console. public class ExampleClass { public sealed class Path { private Path(){} private static char[] badChars = {'\"'. // The following code sets all the values to A.GetInvalidPathChars()(1) = "A"c Path.Write(c). '>'}.GetInvalidPathChars()(2) = "A"c ' The following code displays the elements of the array to the ' console.

} } } El problema del ejemplo anterior se puede corregir aplicando a la colección badChars readonly (ReadOnly en Visual Basic).GetInvalidPathChars()) { Console. [C#] public sealed class Path { private Path(){} public static readonly char[] InvalidPathChars = {'\"'. // The following code displays the elements of the array to the // console. Path. En el ejemplo siguiente se muestra cómo modificar el método GetInvalidPathChars para devolver un clon de la colección badChars.Clone(). Usar propiedades indizadas en colecciones Sólo debe utilizar una propiedad indizada como miembro predeterminado de una interfaz o una clase de la colección. pero sí se podrán modificar los elementos que la componen.GetInvalidPathChars()[2] = 'A'.Write(c). Un modelo de métodos. como Add. Note that the values have changed. la matriz será de sólo lectura y no podrá cambiarse. '>'.Clone().Path. Como alternativa. No cree familias de funciones en tipos que no sean una colección. '<'. indica que el tipo debe ser una colección. Char()) End Function [C#] public static char[] GetInvalidPathChars() { return (char[])badChars. . se puede clonar la colección badChars antes de devolverla. Si se hace. [Visual Basic] Public Shared Function GetInvalidPathChars() As Char() Return CType(badChars. Item y Count.} No se deben usar campos readonly (ReadOnly en Visual Basic) de matrices. Path. foreach(char c in Path.InvalidPathChars[0] = 'A'.GetInvalidPathChars()[1] = 'A'.'|'}' } //The following code can be used to change the values in the array.GetInvalidPathChars()[0] = 'A'. Path. En el siguiente ejemplo se muestra cómo se pueden cambiar los elementos de la matriz de sólo lectura InvalidPathChars.

myObj(i)) Next i [C#] for (int i = 0. } } La regla general es que las matrices nulas. vea el tema Propiedades y métodos.Length > 0 Then ' Do something else. un usuario puede suponer que el siguiente código funcionará.Count .1 DoSomething(obj. se crearán 2 +1 copias de la matriz en el siguiente bucle. i < obj. i++) DoSomething(obj. [Visual Basic] Dim i As Integer For i = 0 To obj. .myObj[i]). Devuelva una matriz vacía en vez de una referencia nula. Para obtener más información. n cada llamada a la propiedad myObj crea una copia de la matriz. las matrices con cadenas vacías ("") y las matrices vacías (0 elementos) deben ser tratadas del mismo modo.myObj.myObj. Devolver matrices vacías Las propiedades String y Array no deben devolver nunca una referencia nula. En el siguiente ejemplo de código. [Visual Basic] Public Sub DoSomething() Dim s As String = SomeOtherFunc() If s. Este valor puede resultar difícil de comprender en este contexto. if (s.Length > 0) { // Do something else.Count. Por ejemplo. Como resultado. End If End Sub [C#] public void DoSomething() { string s = SomeOtherFunc().Propiedades con valores de matrices Utilice las colecciones para evitar que el código funcione mal.

Decimal. [C#] • • public struct DateTime { public static TimeSpan operator -(DateTime t1. o utilizar shift para escribir una secuencia. Proporcione métodos de sobrecarga de operadores sólo en la clase en la que se definen los métodos. El compilador de C# cumple esta directriz. es un requisito de CLS que todos los tipos que sobrecargan operadores incluyan un método secundario con un nombre apropiado específico al dominio que proporcione la funcionalidad equivalente. DateTime t2) { } public static TimeSpan Subtract(DateTime t1. . No obstante.Instrucciones de uso para sobrecargar operadores En las reglas siguientes se describen las pautas para sobrecargar operadores: • • • • Defina los operadores en tipos de valor que sean tipos de lenguajes lógicos integrados. Utilice la sobrecarga de operadores en los casos en los que el resultado de la operación es obvio. como la Estructura System. La inclusión de un método secundario es un requisito de Common Language Specification (CLS). Utilice las convenciones de firma y de nomenclatura descritas en Common Language Specification (CLS). Por esta razón. tiene sentido poder restar un valor Time de otro valor Time y obtener un TimeSpan. Por ejemplo. El compilador de C# lo hace automáticamente. Proporcione firmas alternativas. El ejemplo siguiente es compatible con CLS. La mayoría de los lenguajes no son compatibles con la sobrecarga de operadores. DateTime t2) { } } La tabla siguiente contiene una lista de símbolos de operadores y los métodos alternativos y nombres de operadores correspondientes. Por ejemplo. si sobrecarga el operador de igualdad (==). no es adecuado utilizar el operador or para crear la unión de dos consultas a la base de datos. también debe sobrecargar el operador distinto de (!=). Sobrecargue los operadores de forma simétrica.

/= -++ .Símbolo operador C++ No está definido No está definido + (binario) .(binario) * (binario) / % ^ & (binario) | && || = << >> No está definido No está definido == > < != >= <= *= -= ^= <<= %= += &= |= .(unario) + (unario) ~ de Nombre de método alternativo ToXxx o FromXxx ToXxx o FromXxx Add Subtract Multiply Divide Mod Xor BitwiseAnd BitwiseOr And Or Assign LeftShift RightShift LeftShift RightShift Equals Compare Compare Compare Compare Compare Multiply Subtract Xor LeftShift Mod Add BitwiseAnd BitwiseOr No hay ninguno asignado Divide Decrement Increment Negate Plus OnesComplement Nombre de operador op_Implicit op_Explicit op_Addition op_Subtraction op_Multiply op_Division op_Modulus op_ExclusiveOr op_BitwiseAnd op_BitwiseOr op_LogicalAnd op_LogicalOr op_Assign op_LeftShift op_RightShift op_SignedRightShift op_UnsignedRightShift op_Equality op_GreaterThan op_LessThan op_Inequality op_GreaterThanOrEqual op_LessThanOrEqual op_MultiplicationAssignment op_SubtractionAssignment op_ExclusiveOrAssignment op_LeftShiftAssignment op_ModulusAssignment op_AdditionAssignment op_BitwiseAndAssignment op_BitwiseOrAssignment op_Comma op_DivisionAssignment op_Decrement op_Increment op_UnaryNegation op_UnaryPlus op_OnesComplement .

Instrucciones para implementar Equals y el operador de igualdad (==) En las reglas siguientes se describen las pautas para implementar el método Equals y el operador de igualdad (==). los métodos Equals y GetHashCode se mantienen sincronizados. Reemplace el método Equals siempre que implemente la Interfaz IComparable. Implemente el operador (==) siempre que reemplace el método Equals. incluso los que implementan el método Equals. vea Implementar el método Equals. De este modo. Tenga en cuenta que debe implementar la sobrecarga de operadores para los operadores de igualdad (==). BigNumber. menor que (<). String.ValueType no se realiza tan bien como la implementación personalizada. Por consiguiente. • • • Para obtener más información sobre el método Equals. Reemplace el operador (==) si el tipo es un tipo base como Point. No inicie excepciones desde los métodos Equals o GetHashCode ni desde el operador de igualdad (==). deberá sobrecargar el operador (==) cuando sea necesario. La mayoría de los tipos de referencia. distinto de (!=). y haga que los dos realicen las mismas acciones. Considere la posibilidad de implementar el método Equals en tipos de valor pues la implementación predeterminada en System. Reemplace el método Equals siempre que implemente el operador (==). no deben reemplazar el operador (==). deberá tener cuidado al implementar el operador (==) en tipos de referencia. y mayor que (>) cuando implemente IComparable. etc. se comporten del mismo modo que el código escrito por el usuario con el operador (==). Siempre que considere la posibilidad de sobrecargar los operadores de adición (+) y de sustracción (-). Implementar el operador de igualdad (==) en tipos de referencia En la mayoría de los lenguajes de programación no hay un modelo de implementación predeterminada del operador de igualdad (==) para tipos de referencia. Por consiguiente. . deberá considerar también la sobrecarga del operador (==). • • Implemente el método GetHashCode siempre que implemente el método Equals. Implementar el operador de igualdad (==) en tipos de valor En la mayoría de los lenguajes de programación no hay un modelo de implementación predeterminada del operador de igualdad (==) para tipos de valor. que utilizan el método Equals. Esto permite que el código de infraestructuras como Hashtable y ArrayList.

no un miembro de un objeto. El valor que se convierte debe representar todo el objeto. No convierta valores de dominios diferentes. no es correcto convertir un Button en una cadena mediante la devolución de su título. Por ejemplo. Por ejemplo. no debe haber una conversión implícita de Double a Int32. los números y las cadenas pertenecen a distintos dominios. Proporcione conversiones que funcionen en todo el objeto.gif" a un objeto Bitmap. No genere un valor que sea semánticamente distinto. Las conversiones funcionan dentro de un dominio específico de valores. es conveniente convertir un valor DateTime o TimeSpan a un valor Int32. Tiene sentido convertir un valor Int32 a Double. Sin embargo. ya que estos dos valores se encuentran en dominios diferentes. El valor Int32 sigue representando el tiempo o la duración. pero puede haber una de Int32 a Int64. • • • • . Por ejemplo. Por ejemplo. sin embargo convertir una cadena de nombre de archivo como "c:\mybitmap.Instrucciones de tipos de conversión En las reglas siguientes se describen las instrucciones de uso de conversiones: • No permita conversiones implícitas que den como resultado una pérdida de precisión. no tiene sentido convertir un valor Int32 a String. No inicie excepciones en conversiones implícitas porque es muy difícil que el programador comprenda lo que está pasando. No tiene sentido.

Public Class Base Implements IDisposable ' Implement IDisposable. antes de que el recolector de elementos no utilizados libere el objeto. Por consiguiente.SuppressFinalize(Me) End Sub Protected Overloads Overridable Sub Dispose(disposing As Boolean) . Si el recurso externo es insuficiente o resulta muy caro. deberá proporcionar las formas explícita e implícita de liberar esos recursos. se debe proporcionar la limpieza implícita utilizando el método Finalize. El recolector de elementos no utilizados llama a estos métodos en algún momento. El consumidor del objeto deberá llamar a este método una vez hecho esto utilizando el objeto. se puede conseguir un mejor rendimiento si el programador libera explícitamente los recursos cuando ya no se utilizan. conexiones a bases de datos. Para obtener más información sobre la implementación de los métodos Finalize y Dispose para limpiar los recursos no administrados. En el siguiente ejemplo de código se muestra el modelo de diseño básico de implementación del método Dispose.Modelos de diseño comunes En este tema se proporcionan las instrucciones de implementación de modelos de diseño comunes en las bibliotecas de clases. Finalize proporciona una copia de seguridad para evitar que los recursos se pierdan permanentemente si el programador no llama al método Dispose. etc. [Visual Basic] ' Design pattern for a base class. Proporcione el control implícito implementando el Método Finalize protegido en un objeto (sintaxis de destructor de C# y de Extensiones administradas de C++). Public Overloads Sub Dispose() Implements IDisposable.Dispose Dispose(True) GC. Se puede llamar al método Dispose aunque todavía existan referencias al objeto. En algunos casos. Implementar Finalize y Dispose para limpiar recursos no administrados A menudo. Para proporcionar el control explícito. cuando ya no hay ninguna referencia válida al objeto. se puede proporcionar a los programadores la utilización de un objeto con capacidad para liberar explícitamente estos recursos externos. como identificadores de ventanas (HWND). vea Programar para la recolección de elementos no utilizados. las instancias de clases encapsulan el control sobre los recursos que no administra el motor de tiempo de ejecución. se implementa el método Dispose que proporciona la interfaz IDisposable. Hay que tener en cuenta que cuando se proporciona el control explícito mediante el método Dispose.

' Set large fields to null. ' Call Dispose on your base class.If disposing Then ' Free other state (managed objects). . public class Base: IDisposable { //Implement IDisposable. Dispose (False) End Sub End Class ' Design pattern for a derived class. End If ' Release unmanaged resources. public void Dispose() { Dispose(true). Mybase. GC. End Sub Protected Overrides Sub Finalize() ' Simply call Dispose(False). ' Set large fields to null.Dispose(disposing) End Sub ' The derived class does not have a Finalize method ' or a Dispose method with parameters because it inherits ' them from the base class. Public Class Derived Inherits Base Protected Overloads Overrides Sub Dispose(disposing As Boolean) If disposing Then ' Release managed resources.SuppressFinalize(this). End If ' Free your own state (unmanaged objects). End Class [C#] // Design pattern for a base class.

} } // Free your own state (unmanaged objects). } // Design pattern for a derived class.Dispose(disposing). // Release unmanaged resources. en la encapsulación de un archivo se puede utilizar Close como el nombre del método. es más adecuado utilizar un nombre específico del dominio que utilizar Dispose. Personalizar el nombre del método Dispose En algunos casos. vea Implementar un método Dispose. Dispose (false). base. . Por ejemplo. // Set large fields to null. } // The derived class does not have a Finalize method // or a Dispose method with parameters because it inherits // them from the base class. ~Base() { // Simply call Dispose(false). } Para obtener un ejemplo de código más detallado del modelo de diseño de implementación de los métodos Finalize y Dispose. } // Use C# destructor syntax for finalization code. // Set large fields to null. // Call Dispose on your base class.} protected virtual void Dispose(bool disposing) { if (disposing) { // Free other state (managed objects). public class Derived: Base { protected override void Dispose(bool disposing) { if (disposing) { // Release managed resources.

Se puede reemplazar Close con un nombre de método adecuado al dominio. En el siguiente ejemplo de código se muestra este modelo. El método Finalize de un objeto debe liberar todos los recursos externos que posea el objeto. El método Finalize no debe hacer referencia a ningún otro objeto. ' A derived class should not be allowed ' to override this method. Nota Al método Finalize de la clase base se llama automáticamente con la sintaxis de destructor de C# y de las Extensiones administradas de C++. // A derived class should not be allowed // to override this method. No llame directamente al método Finalize de un objeto que no sea de la clase base del objeto. No haga más visible el método Finalize. Además.En este caso. Dispose() End Sub [C#] // Do not make this method virtual. Hay algunos costos de rendimiento asociados con los métodos Finalize. se implementa Dispose de forma privada y se crea un método Close público que llama a Dispose. considere la posibilidad de implementar IDisposable para que los usuarios de la clase puedan evitar el costo de invocar el método Finalize. public void Close() { // Call the Dispose method with no parameters. } Finalize En las reglas siguientes se describen las instrucciones de uso del método Finalize: • • • • Implemente Finalize sólo en objetos que requieran finalización. Public Sub Close() ' Call the Dispose method with no parameters. Desechar En las reglas siguientes se describen las instrucciones de uso del método Dispose: • • . Ésta no es una operación válida en el lenguaje de programación C#.Finalize desde un método Finalize del objeto. [Visual Basic] ' Do not make this method overridable. un método Finalize debe liberar sólo los recursos que contiene el objeto. Dispose(). Este método debe ser de tipo protected y no de tipo public. Llame al método base. Si requiere un método Finalize.

que liberarán los recursos externos. La excepción a esta regla son las situaciones muy poco habituales en las que el trabajo que no realiza el método Dispose se debe hacer con el método Finalize. Los recursos no administrados que contiene un tipo también se deben liberar en un método Finalize. Finalize se debe implementar en todos los tipos derivados con recursos que es necesario limpiar. Permita que se llame al método Dispose más de una vez sin iniciar excepciones. Llame al método Dispose de la clase base si implementa la interfaz IDisposable. Los usuarios pueden liberar recursos externos llamando al método Dispose público.• Implemente el modelo de diseño Dispose en un tipo que encapsula recursos que deben ser liberados explícitamente. En este caso. • • • • • • • • • . La acción de volver a crear un objeto una vez desechado es un modelo difícil de implementar. El método Dispose debe liberar todos los recursos que contenga un objeto y todos los objetos que contenga ese objeto. los dos creados por TextReader sin el conocimiento del usuario. Esta regla no se aplica al método Dispose porque se debería poder llamarlo varias veces sin iniciar una excepción.SuppressFinalize. Inicie una excepción ObjectDisposedException a partir de los métodos de instancias de este tipo (distintos de Dispose) una vez que se haya deshecho de los recursos. Si el tipo base contiene un método Close. éste deberá llamar a su vez al método Dispose en los objetos Stream y Encoding. Por ejemplo. esto suele indicar que se debe implementar el método Dispose. Propague las llamadas al método Dispose a través de la jerarquía de tipos base. No dé por supuesto que se llamará al método Dispose. puede crear un objeto como TextReader que contenga los objetos Stream y Encoding. Una vez llamado el método Dispose en una instancia. Libere todos los recursos desechables que contenga un tipo en su método Dispose. aunque la clase base no contenga ninguno. Además. en el caso de que no se llame al método Dispose. Considere la posibilidad de prohibir la utilización de un objeto después de llamar al método Dispose. Cuando llame al método Dispose en TextReader. Implemente el modelo de diseño Dispose en un tipo base que habitualmente tiene tipos derivados que contienen recursos. El método no debe realizar ninguna acción después de la primera llamada. evite ejecutar el método Finalize mediante una llamada al Método GC. no implemente un método Finalize en el tipo base. Stream y Encoding pueden adquirir recursos externos.

considere la posibilidad de reemplazar el método Equals para obtener un rendimiento mayor que el que proporciona la implementación predeterminada del método Equals en System. Si implementa un tipo de valor. Este tipo de implementaciones de Equals devuelve el valor true si los dos objetos tienen el mismo valor.Equals(z)) devuelve el valor true si y solo si x. String. es conveniente realizar una comprobación de Equals para comprobar así la igualdad de valores en lugar de la igualdad referencial. x. Si reemplaza Equals y el lenguaje es compatible con la sobrecarga de operadores. Por ejemplo. BigNumber. un reemplazo del método Equals de una clase derivada deberá llamar a la implementación heredada de Equals. La definición de lo que constituye el valor de un objeto está en función del implementador del tipo.Equals(y) && y.Equals(z) devuelve el valor true. Si programa en un lenguaje que es compatible con la sobrecarga de operadores y elige sobrecargar el operador de igualdad (==) para un tipo especificado. este tipo deberá reemplazar el método Equals. Siga el contrato definido en el Método Object. devuelva un valor false para un argumento nulo.Implementar el método Equals Para obtener más información sobre la implementación del operador de igualdad (==). pero suele ser una parte o la totalidad de los datos almacenados en las variables de la instancia del objeto.ValueType. aunque no sean la misma instancia.Equals(y) devuelve el mismo valor que y. No inicie una excepción en la implementación de un método Equals. (x. vea Instrucciones para implementar Equals y el operador de igualdad (==). debe considerar la posibilidad de reemplazar el método Equals en un tipo de referencia si el tipo de referencia se parece a un tipo base como Point. En algunas clases de objetos. Las invocaciones sucesivas de x. x.Equals(y) devuelven el mismo valor siempre que no se modifiquen los objetos a los que se hace referencia mediante x e y. El seguimiento de esta instrucción garantiza que el código de la biblioteca de clases que utiliza el método Equals (como ArrayList y Hashtable) funciona de manera coherente con respecto a la forma en que el código de la aplicación utiliza el operador de igualdad. Sin embargo.Equals(null) devuelve el valor false. Este tipo de implementaciones del método Equals debe devolver los mismos resultados que el operador de igualdad. si implementa un tipo de • • • • . • • • Reemplace el método GetHashCode para permitir que un tipo que funcione correctamente en una tabla hash.Equals de la forma siguiente: • • • • • • x. En vez de esto. Cuando el método Equals de una clase base proporciona igualdad de valores. el valor de una cadena se basa en los caracteres de la cadena. el método Equals de la clase String devuelve un valor true en el caso de que dos cadenas de la cadena contengan exactamente los mismos caracteres y en el mismo orden. debe sobrecargar el operador de igualdad del tipo de valor.Equals(x) devuelve true. Si implementa tipos de referencia. etc. aunque reemplacen el método Equals.Equals(x). La mayoría de los tipos de referencia no deben sobrecargar el operador de igualdad.

deberá reemplazar el método Equals en ese tipo.Object() Console. obj1 = obj2.Equals(obj2)) End Sub End Class [C#] using System.Equals(obj2)). deberá reemplazar el operador de igualdad. como un tipo de número complejo.Equals(obj2)). False True Reemplazar el método Equals .WriteLine(obj1. Object obj2 = new Object(). • Si implementa la Interfaz IComparable en un tipo dado. [Visual Basic] Imports System Class SampleClass Public Shared Sub Main() Dim obj1 As New System.WriteLine(obj1. Ejemplos Implementar el método Equals El siguiente ejemplo de código contiene dos llamadas a la implementación predeterminada del método Equals.Object() Dim obj2 As New System. Console. Console.referencia destinado a contener semántica de valores. class SampleClass { public static void Main() { Object obj1 = new Object().Equals(obj2)) obj1 = obj2 Console.WriteLine(obj1.WriteLine(obj1. } } El resultado del código anterior es el siguiente.

Equals invoca a Point.Equals(obj) And z = CType(obj. If obj Is Nothing Or Not Me.y = p. [Visual Basic] Imports System Class Point Private x As Integer Private y As Integer Public Overrides Overloads Function Equals(obj As Object) As Boolean ' Check for null values and compare run-time types. Point3D. Point) Return Me.Equals porque Point implementa Equals de forma que se proporciona igualdad de valores.x And Me. Dado que el reemplazo de la clase Point por parte de Equals es el primero en la cadena de herencia para introducir la igualdad de valores.x = p. Point3D). no se invoca al método Equals de la clase base (que se hereda de Object y comprueba la igualdad referencial).GetType() Is obj.GetHashCode() ^ z End Function End Class [C#] using System.z End Function Public Overrides Function GetHashCode() As Integer Return MyBase.y End Function End Class Class Point3D Inherits Point Private z As Integer Public Overrides Overloads Function Equals(obj As Object) As Boolean Return MyBase.GetType() Then Return False End If Dim p As Point = CType(obj.En el siguiente ejemplo de código se muestra una clase Point que reemplaza el método Equals para proporcionar igualdad de valores y una clase Point3D derivada de Point. . No obstante.

if (obj == null || GetType() != obj.y). aunque obj y la instancia actual no tengan el mismo tipo en tiempo de ejecución.GetHashCode() ^ z. public override bool Equals(Object obj) { // Check for null values and compare run-time types. Si en vez de esto el método ha utilizado una comprobación de la forma obj is Point. Point p = (Point)obj. El método Equals utiliza el Método Object. el método devuelve el valor false.GetType para determinar si los tipos en tiempo de ejecución de los dos objetos son idénticos. public override bool Equals(Object obj) { return base. la comprobación devolverá el valor true en los casos en los que obj sea una instancia de una clase derivada de Point. Una vez comprobado que ambos .Equals comprueba que el argumento obj no es null y que hace referencia a una instancia del mismo tipo que este objeto. } } El método Point.z. Si alguna de las comprobaciones no es satisfactoria.x) && (y == p. y.Equals(obj) && z == ((Point3D)obj). return (x == p.class Point: object { int x. } public override int GetHashCode() { return x ^ y.GetType()) return false. } public override int GetHashCode() { return base. } } class Point3D: Point { int z. Conviene señalar que typeof (TypeOf en Visual Basic) no se utiliza aquí porque devuelve el tipo estático.

como se muestra en el siguiente ejemplo de código. public override bool Equals(Object obj) { .Equals.b) End Function Public Overrides Function GetHashCode() As Integer Return a.Equals(r. Sólo cuando el método Equals heredado devuelva el valor true. En Point3D. b. El método Equals heredado comprueba si obj no es null. Concretamente. si obj es una instancia de la misma clase que el objeto y si las variables de la instancia heredada coinciden. class Rectangle { Point a. Rectangle) ' Use Equals to compare instance variables.GetHashCode() End Function End Class [C#] using System.GetType() Then Return False End If Dim r As Rectangle = CType(obj. [Visual Basic] Imports System Class Rectangle Private a.a) And Me. Utilizar el método Equals para comparar variables de instancia En el ejemplo anterior.Equals(r. En algunos casos. la conversión a Point3D no se ejecuta salvo que se haya determinado que obj es de tipo Point3D o una clase derivada de Point3D.GetType() Is obj. Return Me. debe utilizarse el método Equals para comparar variables de instancia en una implementación de Equals.objetos son del mismo tipo. el método convierte obj en el tipo Point y devuelve el resultado de la comparación de las variables de instancia de los dos objetos. se invoca al método Equals heredado antes de realizar otras acciones.GetHashCode() ^ b.a. comparará el método las variables de instancia introducidas en la clase derivada.b. el operador de igualdad (==) se utiliza para comparar las variables de instancia individuales. b As Point Public Overrides Overloads Function Equals(obj As [Object]) As Boolean If obj Is Nothing Or Not Me.

a) && b. } } Sobrecargar el operador de igualdad (==) y el método Equals Algunos lenguajes de programación. Esto se consigue normalmente escribiendo el método Equals en términos del operador de igualdad (==) sobrecargado.GetHashCode(). como en el siguiente ejemplo.GetHashCode() ^ b. } public override int GetHashCode() { return re.b).GetType()) return false. } public override int GetHashCode() { return a. } public static bool operator ==(Complex x.if (obj == null || GetType() != obj. return a. Complex y) { return !(x == y).GetHashCode().im. también debe reemplazar el método Equals para proporcionar la misma funcionalidad.Equals(r. im. public override bool Equals(Object obj) { return obj is Complex && this == (Complex)obj. Rectangle r = (Rectangle)obj.re && x. admiten la sobrecarga de operadores. Cuando un tipo sobrecarga el operador (==).re == y. [C#] public struct Complex { double re. } } . como el lenguaje C#.im == y. } public static bool operator !=(Complex x. // Use Equals to compare instance variables.GetHashCode() ^ im.Equals(r. Complex y) { return x.

se sabe que no se derivará ninguna clase de Complex. Por tanto. el método Equals no necesita comparar los resultados de GetType para cada objeto. En su lugar utiliza el operador is para comprobar el parámetro obj.Como Complex es un tipo de valor struct de C#. .

Otra posibilidad es utilizar los tiempos de espera como una propiedad de la clase de servidor.PerformOperation(timeout).PerformOperation(timeout) [C#] server. como se muestra a continuación. Delegados Utilice un delegado si se cumplen las siguientes condiciones: • • • Desea un puntero a función con estilo de lenguaje C.PerformOperation() [C#] server. Normalmente. [Visual Basic] server. hay más de un objeto que requiere la notificación del evento. a través de métodos Add y Remove distintos. Interfaces Utilice una interfaz si la función de devolución de llamada requiere un comportamiento complejo. Cada tipo tiene sus propias características de uso que se adaptan perfectamente a situaciones específicas. Desea una función de devolución de llamada sencilla. Los tiempos de espera pueden adoptar el formato de un parámetro en la llamada al método. Desea que los usuarios finales puedan agregar fácilmente un agente de escucha a la notificación en el diseñador visual. Desea que el registro se produzca en la llamada o durante la construcción y no a través de un método Add distinto. las Interfaces y los Eventos permiten proporcionar la funcionalidad de devolución de llamada. . Uso de tiempos de espera Utilice los tiempos de espera para especificar el tiempo máximo que un llamador está dispuesto a esperar para finalizar una llamada al método. [Visual Basic] server. como se muestra a continuación.Timeout = timeout server.Timeout = timeout. normalmente. Eventos Utilice un evento si se cumplen las siguientes condiciones: • • • Un método solicita por adelantado la función de devolución de llamada.Uso de las funciones de devolución de llamada Los Delegados.

15. la .MaxValue. Resulta complicado utilizar números enteros. Tradicionalmente. ya que la unidad del tiempo de espera no es evidente y es difícil convertir las unidades de tiempo a los milisegundos que se utilizan habitualmente.15. Conviene utilizar el primer enfoque. los tiempos de espera se han representado mediante enteros. End Sub End Class Public Class TestClass Dim server As New Server().server. TimeSpan resuelve los problemas con los números enteros de los tiempos de espera mencionados anteriormente. server.PerformOperation(New TimeSpan(0. } } public class TestClass { public Server server = new Server().PerformOperation(new TimeSpan(0. El mejor enfoque es utilizar la estructura TimeSpan como tipo de tiempo de espera. Si el tiempo de espera es TimeSpan. el método deberá iniciar una excepción si la operación no se completa inmediatamente.0)) End Class [C#] public class Server { void PerformOperation(TimeSpan timeout) { // Insert code for the method here. } Si el tiempo de espera se establece en TimeSpan(0).0)). server. [Visual Basic] Public Class Server Public Sub PerformOperation(timeout As TimeSpan timeout) ' Insert code for the method here. En el siguiente ejemplo de código se muestra cómo utilizar un tiempo de espera de tipo TimeSpan.PerformOperation(). porque la asociación entre la operación y el tiempo de espera es más clara. El enfoque basado en la propiedad puede mejorar si la clase de servidor se diseña para ser un componente que se utilice con diseñadores visuales.

En el siguiente ejemplo de código se incluye una propiedad OperationTimeout estática de tipo TimeSpan que devuelve defaultTimeout. como si no se hubiese establecido el tiempo de espera.operación debe esperar siempre sin que se agote el tiempo de espera.PerformOperation(OperationTimeout) End Sub Overloads Sub PerformOperation(timeout As TimeSpan) ' Insert code here. aunque deberá iniciar una InvalidArgumentException si no admite el valor de tiempo espera especificado. } void PerformOperation(TimeSpan timeout) { // Insert code here. } . Si se agota el tiempo de espera y se inicia una excepción.PerformOperation(OperationTimeout). Si se utiliza un tiempo de espera predeterminado. void PerformOperation() { this. la clase de servidor deberá incluir una propiedad defaultTimeout estática que se utilizará si el usuario no la especifica. [Visual Basic] Class Server Private defaultTimeout As New TimeSpan(1000) Overloads Sub PerformOperation() Me. No es necesario que la clase de servidor sea compatible con ninguno de estos valores. la clase de servidor deberá cancelar la operación subyacente. End Sub ReadOnly Property OperationTimeout() As TimeSpan Get Return defaultTimeout End Get End Property End Class [C#] class Server { TimeSpan defaultTimeout = new TimeSpan(1000).

Esto se muestra en el siguiente ejemplo de código: [Visual Basic] } Sub OnReceiveCompleted(ByVal sender As System. . Para obtener más información.EndReceive(asyncResult. Además.Body))) queue. MessageQueue) ' The following code will throw an exception ' if BeginReceive has timed out. es conveniente iniciar una excepción cuando se agota el tiempo de espera.0. En este caso.TimeSpan OperationTimeout { get { return defaultTimeout. Al redondear a un número distinto de cero se evitan los bucles "busy-wait". En el caso de una operación asincrónica con un tiempo de espera.AsyncResult) Console. queue.Body). ReceiveCompletedEventArgs asyncResult) { MessageQueue queue = (MessageQueue) sender. Dim message As Message = queue. La excepción a esta regla es cuando se redondea un valor a cero. Por ejemplo. el tiempo de espera debe redondearse hacia arriba al valor de tiempo de espera mínimo posible.WriteLine(("Message: " + CStr(message. en los que un tiempo de espera de valor cero provoca un uso del procesador del 100 por ciento. 0)) End Sub [C#] void OnReceiveCompleted(Object sender. vea Instrucciones para provocar y controlar errores.BeginReceive(new TimeSpan(1.Object. se debe llamar a la función de devolución de llamada e iniciar una excepción la primera vez que se tiene acceso a los resultados de la operación. Console.EndReceive(asyncResult. un tipo que sólo puede esperar en incrementos de un segundo deberá redondear al segundo más cercano. ByVal asyncResult As ReceiveCompletedEventArgs) Dim queue As MessageQueue = CType(sender. 0. } } Los tipos que no pueden resolver los tiempos de espera de un TimeSpan deberán redondear el tiempo de espera al intervalo más cercano.0)). Si se agota el tiempo de espera significa que la operación no se ha podido completar correctamente y. Message message = queue.BeginReceive(New TimeSpan(1.WriteLine("Message: " + (string)message. en vez de devolver un código de error. se debe tratar y administrar como cualquier otro error en tiempo de ejecución. por tanto.AsyncResult). // The following code will throw an exception // if BeginReceive has timed out.

. vea Instrucciones para programación asincrónica.} Para obtener más información.

esto no debe ocurrir si las bibliotecas de clases se escriben correctamente con relación a la seguridad. Es importante que las clases protejan los recursos. una clase de plena confianza expone un recurso protegido mediante un permiso.FileStream. las API del sistema o de COM. Una biblioteca de clases que realiza operaciones en recursos protegidos debe exigir esta protección Antes de realizar alguna acción en la solicitud de un recurso protegido. Código de biblioteca de clases de plena confianza En la mayoría de las bibliotecas de clases se implementa código de plena confianza que encapsula la funcionalidad específica de la plataforma como objetos administrados. Si el llamador no tiene permiso.Seguridad en las bibliotecas de clases Los diseñadores de bibliotecas de clases deben comprender la seguridad de acceso a código para escribir bibliotecas de clases seguras. La Clase File utiliza una . normalmente. Proteger objetos con permisos Los permisos se definen para proteger recursos específicos. Un ejemplo típico de este tipo de recurso es un archivo. por lo general. Cuando se escribe una biblioteca de clases. aunque los datos reales se recuperen de una caché en memoria y no se produzca ninguna operación real en el archivo. mediante el recorrido de pila) para eliminar el recurso. Por ejemplo. En un escenario habitual de seguridad de bibliotecas de clases. en primer lugar. para tener acceso al recurso se utiliza una API de código nativo. Si el llamador tiene el permiso adecuado. todos los llamadores. hay que tener en cuenta dos principios de seguridad: proteger los objetos con permisos y escribir código de plena confianza. y permitiendo que la parte más extensa del código administrado adquiera la seguridad principal en tiempo de ejecución de estas bibliotecas de clases principales. El grado en que se apliquen estos dos principios dependerá de la clase que se escriba. no sólo contra el acceso directo. por ejemplo. en el código con una comprobación declarativa o una comprobación imperativa de los permisos apropiados. Dado que el código de bibliotecas de clases tiene acceso con bastante frecuencia a recursos protegidos y código no administrado. El código de bibliotecas de clases a menudo es código de plena confianza o como mínimo de alta confianza. permitirá completar la acción. sino también contra cualquier tipo de exposición posible. cualquier defecto del código representa una grave amenaza a la integridad de todo el sistema de seguridad. representan objetos que es necesario proteger con permisos. colocando una alta carga de seguridad en un conjunto de bibliotecas de clases relativamente pequeño. el código de la biblioteca de clases debe comprobar. No obstante. la acción no debe completarse y se debe provocar una excepción de seguridad. System. La implementación de estas clases es responsable de comprobar los permisos de llamadores y de permitir sólo a los llamadores autorizados realizar operaciones para las que tengan permisos. como eliminar un archivo. El código de plena confianza puede exponer una debilidad en la seguridad de todo el sistema. Para obtener más información.IO. siga las instrucciones que se describen en este tema cuando escriba código de bibliotecas de clases. que el llamador tiene permiso (y. Algunas clases. La protección se implementa. como la Clase System. el objeto de un archivo almacenado en la caché es responsable de comprobar los permisos de lectura del archivo. Esto es así porque el efecto de pasar datos al llamador es el mismo que si el llamador realiza una operación de lectura real. Para minimizar los peligros relacionados con la seguridad. vea Escribir bibliotecas de clases seguras.Security (Espacio de nombres) contiene clases que ayudan a realizar estas comprobaciones en las bibliotecas de clases que se escriban.

La clase File declara FullTrust para llamar al código nativo. está técnica da como resultado una mejora del rendimiento si se realizan tres o más comprobaciones de permisos cada vez. una aserción debe ir seguida de una comprobación de los permisos del llamador como se ha descrito anteriormente en este tema. no están disponibles para el llamador pueden provocar problemas en la seguridad.API nativa para realizar operaciones en el archivo. Por tanto. y la solicitud de eliminación del archivo se completa correctamente. 4.txt llamando al Método File. Si un llamador no tiene suficientes permisos. La clase File utiliza una API nativa para realizar la operación de eliminación del archivo. . Un llamador solicita la eliminación del archivo c:\test. Si una operación consiste en realidad en un número de acciones en un nivel inferior en el que es necesario realizar comprobaciones de seguridad. el rendimiento puede mejorar enormemente si se comprueban los permisos de los llamadores una vez y. Además. estas operaciones pueden tener un costo muy elevado. 3. siempre que el código de confianza declare un permiso. se realizan los pasos siguientes. Además. Rendimiento Las comprobaciones de seguridad incluyen la comprobación de los permisos de los llamadores en la pila. un ensamblado puede contener clases que no necesitan permisos especiales. como la eliminación. Sin embargo. Al código de plena confianza se concede implícitamente el resto de los permisos. Independientemente de la protección de recursos. Diseñe código de confianza para que se pueda llamar mediante un código de confianza parcial del sistema sin exponer puntos vulnerables de seguridad. 1. si no se les ha concedido el permiso. Precauciones para código de alta confianza El código de una biblioteca de clases de confianza concede permisos que no están disponibles en el código de la mayoría de las aplicaciones. Además. se provoca una excepción de seguridad. El método Delete crea un objeto de permiso que representa el permiso delete c:\test.Delete. cualquier aspecto de la interfaz de programación que pueda descifrar la seguridad de tipos o permitir el acceso de datos que. La clase File devuelve a su llamador. por lo general. se declara el permiso necesario antes de realizar acciones. los recursos se protegen mediante el recorrido de la pila de todos los llamadores. 2. permite infringir las reglas de seguridad de tipos y de uso de objetos. Esta aserción detendrá la propagación del recorrido por la pila en un punto para efectuar correctamente la comprobación. porque sus llamadores no tienen este permiso.txt. el código tiene la responsabilidad de comprobar los permisos requeridos. se debe tener un cuidado especial cuando se escribe código de plena confianza y código de alta confianza. Generalmente. Normalmente. a continuación. pero que se conceden porque el ensamblado contiene otras clases que requieren estos permisos. 6. 5. En función de la profundidad de la pila. Normalmente. El código de la clase File comprueba todos los llamadores de la pila para ver si tienen el permiso solicitado. Para proteger el recurso. se bloquea el intento de acceso. se debe minimizar el número de aserciones de permisos para reducir el riesgo de exposiciones inintencionadas. Estas situaciones pueden exponer una debilidad en la seguridad del sistema.

No exponga la funcionalidad en una clase que permita a un llamador de confianza parcial aprovecharse de la confianza superior de la clase. • • • • . pues pueden ser utilizadas para omitir la seguridad en otros lugares.Resumen de los problemas de seguridad de las bibliotecas de clases • • • En las bibliotecas de clases que utilizan recursos protegidos se debe garantizar que sólo se utilizan estos recursos en los permisos de los llamadores. agregue operaciones que incluyan comprobaciones de seguridad y considere la posibilidad de utilizar aserciones para limitar los recorridos de la pila sin poner en peligro la seguridad. La aserción de permisos se debe efectuar sólo cuando es necesario y debe ir precedida de las necesarias comprobaciones de permisos. Para mejorar el rendimiento. Tenga en cuenta que un llamador malicioso de confianza parcial puede en potencia utilizar una clase para omitir la seguridad. No defina interfaces sin seguridad de tipos. No dé por supuesto que sólo los llamadores con permisos específicos llamarán al código.

Operaciones de cadenas que tengan en cuenta las referencias culturales y la seguridad Las operaciones de cadena que tienen en cuenta la referencia cultural proporcionada por . el alfabeto turco tiene dos versiones del carácter I: una con un punto y otra sin él. De manera predeterminada. Por ejemplo. La comparación devuelve false si CurrentCulture se establece como "tr-TR" (turco de Turquía). que tendría éxito en la mayoría de las referencias culturales. Sin embargo. Puntos vulnerables en la seguridad en operaciones con cadenas que tengan en cuenta la referencia cultural Las exclusivas reglas de asignación de mayúsculas y minúsculas del alfabeto turco muestran cómo se puede usar una operación que tiene en cuenta la referencia cultural para crear un punto vulnerable en la seguridad del código de una aplicación. el carácter i (Unicode 0069) es la versión minúscula del carácter I (Unicode 0049). En turco. una comparación de cadenas que no tenga en cuenta las mayúsculas y minúsculas de los caracteres i (Unicode 0069) e I (Unicode 0049). (Unicode 0131). En el siguiente ejemplo de código se muestra cómo una operación String. Estos cambios de comportamiento pueden producirse si se cambia la referencia cultural o si la referencia cultural del equipo en el que se ejecuta la operación es distinta de la que utilizó el programador para probar el código. En la mayoría de los alfabetos latinos. Sin embargo. el carácter I (Unicode 0049) se considera la versión mayúscula de un carácter diferente &#x0131. Y el carácter i (Unicode 0069) se considera la versión minúscula de otro carácter &#x0130. realizada sobre las cadenas "FILE" y "file". fallaría para la referencia cultural "tr-TR" (turco de Turquía).Compare.CurrentCulture del subproceso actual. Las operaciones con cadenas que tengan en cuenta la referencia cultural pueden crear puntos vulnerables en la seguridad si el comportamiento esperado por el programador que escribe una biblioteca de clases difiere del comportamiento real de funcionamiento en el equipo en el que se ejecuta la operación. Si una aplicación adopta una decisión de importancia basándose en el resultado de esta operación String. (Unicode 0130). Utilizar operaciones que tienen en cuenta la referencia cultural en escenarios en los que los resultados deberían ser independientes de ésta puede hacer que el código no funcione en referencias culturales con asignaciones personalizadas de mayúsculas y minúsculas y reglas de ordenación. Por consiguiente.NET Framework pueden resultar ventajosas para los programadores que creen aplicaciones diseñadas para mostrar resultados basándose en la referencia cultural.Compare devuelve un resultado que varía según la referencia cultural. debido a las diferencias en criterios de ordenación y asignaciones de mayúsculas y minúsculas usadas por las distintas referencias culturales. [Visual Basic] Imports System . y provocar problemas de seguridad en la aplicación.CurrentCulture se establece como "en-US" (inglés de Estados Unidos). el resultado de esa decisión podría verse trastocado si se cambia el valor de CurrentCulture. La comparación devuelve true si la propiedad Thread.Compare que no tiene en cuenta las mayúsculas y minúsculas. los métodos que tienen en cuenta las referencias culturales obtienen la referencia cultural que van a usar a partir de la propiedad CultureInfo. las operaciones de cadena que tienen en cuenta la referencia cultural no son siempre el comportamiento más deseable. el método String. obtiene resultados distintos dependiendo de la referencia cultural.

CurrentCulture.CurrentCulture = New CultureInfo("en-US") Console. Console.DisplayName) Console.CurrentThread.CurrentThread. String. Thread. _ "FILE". "FILE".CurrentCulture.WriteLine("(file == FILE) = {0}".WriteLine("(file == FILE) = {0}". using System.CurrentThread. Console. True) = 0) End Sub End Class [C#] using System.S. String.Imports System.WriteLine("(file == FILE) = {0}".CurrentThread.CurrentThread.DisplayName). .Compare("file".Threading.DisplayName) Console. public class TurkishISample { public static void Main() { // Set the CurrentCulture property to English in the U.CurrentThread.Compare("file".CurrentCulture = new CultureInfo("en-US").Globalization Imports System. Thread.S.Threading Public Class TurkishISample Public Shared Sub Main() ' Set the CurrentCulture property to English in the U. (string.CurrentCulture = New CultureInfo("tr-TR") Console.Globalization.CurrentCulture.WriteLine("Culture = {0}". Thread.WriteLine("Culture = {0}". _ "FILE". true) == 0)). True) = 0) ' Set the CurrentCulture property to Turkish in Turkey.Compare("file".WriteLine("Culture = {0}". _ Thread. using System. Thread. _ Thread.

Compare("file". Console. debería realizarse una ordenación que tuviera en cuenta las referencias culturales. vea Asignaciones personalizadas de mayúsculas y minúsculas. se deben realizar operaciones que no tengan en cuenta la referencia cultural. pasando un parámetro CultureInfo.// Set the CurrentCulture property to Turkish in Turkey. los resultados de las operaciones de cadenas no deberían variar en función de la referencia cultural. Use estas sobrecargas para mostrar claramente si se pretende que una operación de cadenas tenga en cuenta las referencias culturales o no. si una aplicación muestra al usuario una lista ordenada de cadenas localizadas en un cuadro de lista.WriteLine("(file == FILE) = {0}". para ello.CurrentCulture. Por ejemplo. Culture = English (United States) (file == FILE) = True Culture = Turkish (Turkey) (file == FILE) = False Para obtener más información sobre reglas de ordenación y asignaciones de mayúsculas y minúsculas personalizadas que puedan provocar incoherencias similares. En general. Thread. proporcionan sobrecargas del método que permiten especificar de forma explícita la referencia cultural que hay que usar. ya que la comparación de i e I que no tiene en cuenta las mayúsculas y minúsculas se evalúa como true para la referencia cultural "en-US" y como false para la referencia cultural "tr-TR". Las operaciones de cadenas que muestran los resultados al usuario final deberían normalmente tener en cuenta las referencias culturales. La mayoría de los métodos .CurrentCulture = new CultureInfo("tr-TR"). Por ejemplo. si está trabajando con nombres de archivo. formatos de persistencia o información simbólica que no se muestra al usuario final. } } Los siguientes resultados en la consola muestran cómo éstos pueden cambiar en función de la referencia cultural. y reglas de ordenación. si una aplicación compara una cadena para determinar si es una etiqueta XML reconocida. (string. especifique la propiedad CurrentCulture para el parámetro CultureInfo.CurrentThread.CurrentThread. Los resultados de las operaciones de cadenas que se usan internamente no deberían normalmente tener en cuenta las referencias culturales. la comparación no debería tener en cuenta las referencias culturales.WriteLine("Culture = {0}". basta con .Thread.NET Framework que realizan operaciones de cadenas que tienen en cuenta las referencias culturales de manera predeterminada. Para eliminar los puntos de seguridad vulnerables producidos por variaciones de la referencia cultural en las reglas de ordenamiento y asignaciones de mayúsculas y minúsculas. Especificar la referencia operaciones de cadenas cultural explícitamente en El hecho de si las operaciones de cadenas deben tener en cuenta las referencias culturales o no depende de cómo use los resultados la aplicación. "FILE". Console. true) == 0)). En operaciones que tienen en cuenta las referencias culturales.DisplayName).

con el mismo comportamiento que durante las pruebas. Para determinar si se debe especificar CurrentCulture (los resultados tienen en cuenta la referencia cultural) o InvariantCulture (los resultados no tienen en cuenta la referencia cultural). Cuando se utilicen estas API. Char. String.ToLower (Método) Char. Realizar cambios de mayúsculas y minúsculas que no tienen en cuenta las referencias culturales describe cómo utilizar los métodos String.RegularExpressions (Espacio de nombres) Los temas siguientes proporcionan más información sobre estas API y ejemplos que muestran cómo usarlas correctamente para obtener resultados que no tengan en cuenta las referencias culturales: • Realizar comparaciones de cadenas que no tienen en cuenta las referencias culturales describe cómo utilizar los métodos String.Text.Sort (Método) | Array. Realizar operaciones de cadenas que no distinguen entre referencias culturales Las siguientes API de . sin importar su referencia cultural. Realizar operaciones de cadenas que no tienen en cuenta las referencias culturales en colecciones describe cómo utilizar la clase CaseInsensitiveComparer. el método ArrayList.CreateCaseInsensitiveHashTable (Método) | Array.ToUpper (Método) Char. siga las pautas descritas en Especificar la referencia cultural explícitamente en operaciones de cadenas.InvariantCulture para el parámetro CultureInfo.CompareTo (Método) String.CompareTo para llevar a cabo comparaciones de cadenas que no tengan en cuenta las referencias culturales.BinarySearch (Método) | System.ToUpper.NET Framework realizan operaciones de cadenas que tienen en cuenta la referencia cultural de forma predeterminada.especificar la propiedad CultureInfo.Compare (Método) String.Sort y el • • .ToUpper (Método) String.Compare y String.ToLower (método) CaseInsensitiveComparer (Clase) | CaseInsensitiveHashCodeProvider (Clase) | SortedList (Clase) | ArrayList.ToLower. la clase CaseInsensitiveHashCodeProvider. String. Esto garantiza que el código se ejecutará en todos los equipos.ToLower para llevar a cabo cambios de mayúsculas y minúsculas que no tengan en cuenta las referencias culturales.ToUpper y Char. se deberá usar siempre la sobrecarga de métodos o el constructor de clases que permita especificar de forma explícita la referencia cultural que se tiene que usar. la clase SortedList.Sort (Método) | CollectionsUtil.

Especifique CultureInfo. El estado estático debe ser seguro para la ejecución de subprocesos.CurrentCulture si desea un comportamiento que tenga en cuenta las referencias culturales. Esto abre la posibilidad de errores en el subproceso. En los casos en los que desee proporcionar una versión segura para ejecutar subprocesos.método CollectionsUtil.Sort y Array. Proporcione medios para personalizar la referencia cultural de todos los componentes que usen internamente operaciones que no tengan en cuenta la referencia cultural. Para examinar ejemplos de código que muestra cómo usar el método String. las bibliotecas de clases de . Use el método String. Especifique CultureInfo. • • . el estado estático se comparte entre solicitudes.NET Framework no son de forma predeterminada seguras para la ejecución de subprocesos.CreateCaseInsensitiveHashtable para llevar a cabo operaciones en colecciones que no tengan en cuenta las referencias culturales.Compare en lugar del método String. lo que significa que varios subprocesos pueden ejecutar ese código al mismo tiempo.InvariantCulture si desea un comportamiento que no tenga en cuenta las referencias culturales. lo que minimiza la necesidad de seguridad para subprocesos. con lo que se crea la posibilidad de que se produzcan errores en el bloqueo. tenga en cuenta las pautas siguientes: • Pase de forma explícita la información sobre la referencia cultural a todas las operaciones que tengan en cuenta las referencias culturales. En los modelos habituales de aplicaciones.Compare. No es necesario que el estado de la instancia sea seguro para la ejecución de subprocesos.CompareTo. • • Instrucciones de diseño de subprocesos En las reglas siguientes se describen las instrucciones de diseño de implementación de subprocesos: • Evite proporcionar métodos estáticos que modifiquen el estado estático. De forma predeterminada. Por este motivo. El método String. En los escenarios de servidor habituales. Considere la posibilidad de utilizar un modelo de diseño que encapsule los datos en instancias que no se compartan entre solicitudes. las bibliotecas de clases no deberían ser seguras para la ejecución de subprocesos. • Resumen de las instrucciones de uso de cadenas que tengan en cuenta las referencias culturales Al realizar operaciones en el código de la biblioteca de clases con cadenas que tengan en cuenta las referencias culturales. un solo subproceso ejecuta el código de usuario cada vez.Compare clarifica si se desea que una operación tenga en cuenta o no las referencias culturales.BinarySearch para llevar a cabo operaciones en matrices que no tengan en cuenta las referencias culturales. Realizar operaciones que no tienen en cuenta las referencias culturales en el espacio de nombres describe cómo llevar a cabo operaciones que no tengan en cuenta las referencias culturales usando métodos en el espacio de nombres RegularExpressions. • Realizar comparaciones de cadenas que no tienen en cuenta las referencias culturales en matrices describe cómo utilizar los métodos Array. vea Realizar comparaciones de cadenas que no tienen en cuenta las referencias culturales. La agregación de bloqueos para crear un código seguro para la ejecución de subprocesos reduce el rendimiento y aumenta la contención de bloqueos.

se mejorará el rendimiento.Threading. [Visual Basic] • • SyncLock Me myField += 1 End SyncLock [C#] lock(this) { myField++. vea el método System. Se puede usar el código siguiente. Puede ocurrir que este bloqueo se descubra sólo en condiciones de una gran carga de subprocesos.ArrayList. Para actualizar la variable y hacer el código seguro para subprocesos. [Visual Basic] System. Sin embargo. esto afectará al rendimiento porque habrá una gran cantidad de sincronización redundante. El uso excesivo de una sincronización más flexible y detallada puede tener un impacto negativo en el rendimiento. [Visual Basic] If x Is Nothing Then . Esta clase ejecuta un prefijo lock si no hay contención. Tenga en cuenta las llamadas a métodos en las secciones bloqueadas. Otro ejemplo es actualizar la variable de un tipo de objeto sólo si es null (Nothing en Visual Basic). Evite utilizar bloqueos siempre que sea posible.Interlocked.Interlocked. Tenga en cuenta los problemas con la instrucción lock (SyncLock en Visual Basic). • • Diseñe la biblioteca teniendo en cuenta la carga de ejecución en un escenario de servidor.IsSynchronized. Además. se producirá un bloqueo.Threading.ArrayList.proporcione un método Synchronized estático que devuelva una instancia segura para la ejecución de subprocesos de un tipo.Increment(myField).Increment(myField) [C#] System. Los bloqueos se pueden producir cuando el método estático de una clase A llama a los métodos estáticos de la clase B y viceversa. Si estos métodos no están correctamente factorizados.Threading. Si A y B sincronizan sus métodos estáticos. Resulta más apetecible utilizar la instrucción lock para solucionar todos los problemas de los subprocesos. Para examinar un ejemplo. puede tener un impacto muy negativo en la escalabilidad. En una revisión de código. Se pueden producir problemas de rendimiento cuando un método estático en la clase A llame a un método estático en la clase A. la Clase System. tenga cuidado con instancias como la que se muestra en el ejemplo siguiente.Collections.Collections. } Si se reemplaza el ejemplo anterior con el siguiente.Synchronized y el método System.Interlocked es mejor para las actualizaciones que deban ser atómicas.

es mejor evitar la sincronización.Threading. • Evite la necesidad de sincronización siempre que sea posible. [Visual Basic] System.Interlocked. y. en vez de eliminarlas.Interlocked.CompareExchange(x. .Threading. Algunas veces se puede ajustar el algoritmo para tolerar las condiciones de anticipación.CompareExchange(ref x. En las rutas de mucho tráfico. y. null). } } } Se puede mejorar el rendimiento del ejemplo anterior cambiando el código por lo siguiente.SyncLock Me If x Is Nothing Then x=y End If End SyncLock End If [C#] if (x == null) { lock (this) { if (x == null) { x = y. Nothing) [C#] System.

Entre estos servicios se incluyen los siguientes: • • • • Tipos de sincronización ReaderWriterLock. como contenedores que sean compatibles con el método WaitForMultipleObjects.NET Framework. En este tema se describe el modelo de diseño de programación asincrónica. . Es conveniente que estos servidores sigan el modelo de diseño descrito en este documento para exponer operaciones asincrónicas. La filosofía de estas instrucciones es como sigue: • • El cliente debe decidir si una determinada llamada debe ser asincrónica. La programación asincrónica es un concepto principal en . El tiempo de ejecución proporciona los servicios necesarios para admitir el modelo de programación asincrónica. como el Entorno remoto. primitivos. El servidor puede elegir admitir explícitamente el comportamiento asincrónico para implementar este comportamiento de forma más eficiente que con una arquitectura general. Exposición a la infraestructura subyacente. El motor de tiempo de ejecución debe ser capaz de administrar la diferencia entre el cliente y el servidor. Grupos de subprocesos. o para admitir el comportamiento asincrónico de los clientes. como las secciones críticas e instancias • • • Construcciones de sincronización. Se debe exigir la seguridad de tipos. se evita la situación en la que el servidor tiene que implementar IDispatch y realizar una gran cantidad de trabajo para ser compatible con la invocación dinámica del cliente. como objetos Message y ThreadPool. ASP.Instrucciones para programación asincrónica La programación asincrónica es una función compatible con muchas áreas de Common Language Runtime.NET y los formularios Windows Forms. No es necesario una programación adicional en el servidor para que sea compatible con el comportamiento asincrónico del cliente. En consecuencia.

ref long primefactor1.Modelo de diseño para programación asincrónica En el siguiente ejemplo de código se muestra una clase de servidor que factoriza un número. Dim i As Integer For i = 2 To factorizableNum . .1 If 0 = factorizableNum Mod i Then primefactor1 = i primefactor2 = factorizableNum / i Exit For End If Next i If 1 = primefactor1 Then Return False Else Return True End If End Function End Class [C#] public class PrimeFactorizer { public bool Factorize(long factorizableNum. ByRef primefactor2 As Long) As Boolean primefactor1 = 1 primefactor2 = factorizableNum ' Factorize using a low-tech approach. ByRef primefactor1 As Long. [Visual Basic] Public Class PrimeFactorizer Public Function Factorize(factorizableNum As Long. ref long primefactor2) { primefactor1 = 1. primefactor2 = factorizableNum.

.// Factorize using a low-tech approach. ByRef primefactor1 As Long. } } if (1 == primefactor1 ) return false. else return true . public delegate bool FactorizingAsyncDelegate(long factorizableNum. ref long primefactor2). for (int i=2. Delegate Function FactorizingAsyncDelegate(factorizableNum As Long. Dim pf As New PrimeFactorizer() ' Create a delegate on the Factorize method on the Factorizer. Dim fd As New FactorizingDelegate(pf. PrimeFactorizer pf = new PrimeFactorizer(). break. ByRef primefactor2 As Long) End Sub ' Create an instance of the Factorizer.Factorize) [C#] // Define the delegate.i++) { if (0 == (factorizableNum % i)) { primefactor1 = i. } } En el siguiente ejemplo de código se muestra un cliente que define un modelo de invocación asincrónica al método Factorize de la clase PrimeFactorizer del ejemplo anterior. primefactor2 = factorizableNum / i.i<factorizableNum. ref long primefactor1. [Visual Basic] ' Define the delegate. // Create an instance of the Factorizer.

ByRef primefactor1 As Long. Object AsyncState). El compilador emitirá la clase FactorizingAsyncDelegate siguiente después de analizar la definición en la primera línea del ejemplo anterior. ByRef primefactor1 As Long. ref ulong primefactor1. ref ulong primefactor2). public IAsyncResult BeginInvoke(ulong factorizableNum. ref unsigned long primefactor2.Factorize). cb As AsyncCallback. ByRef primefactor2 As Long. ar As IAsyncResult) As Boolean End Function [C#] public class FactorizingAsyncDelegate : Delegate { public bool Invoke(ulong factorizableNum. ByRef primefactor2 As Long. Esta clase generará los métodos BeginInvoke y EndInvoke. AsyncCallback cb. . AsyncState As Object) As IasyncResult End Function ' Supplied by the compiler. Public Function EndInvoke(ByRef primefactor1 As Long. FactorizingDelegate fd = new FactorizingDelegate(pf. [Visual Basic] Public Class FactorizingAsyncDelegate Inherits Delegate Public Function Invoke(factorizableNum As Long. ByRef primefactor2 As Long) As Boolean End Function ' Supplied by the compiler. ref unsigned long primefactor1. Public Function BeginInvoke(factorizableNum As Long.// Create a delegate on the Factorize method on the Factorizer. // Supplied by the compiler.

[Visual Basic] Delegate Function AsyncCallback(ar As IAsyncResult) ' Returns true if the asynchronous operation has been completed. ref ulong primefactor2. ReadOnly Property AsyncObject() As [Object] ' Get accessor implementation goes here. IAsyncResult ar). public bool EndInvoke(ref ulong primefactor1. Public Interface IasyncResult ' Handle to block on for the results. Para obtener más información. End Property . vea Interfaz IAsyncResult. ReadOnly Property CompletedSynchronously() As Boolean ' Get accessor implementation goes here.// Supplied by the compiler. ReadOnly Property IsCompleted() As Boolean ' Get accessor implementation goes here. End Property ' Returns true if the call completed synchronously.NET Framework. End Property ' The delegate object for which the async call was invoked. End Property ' The state object passed in through BeginInvoke. ReadOnly Property AsyncWaitHandle() As WaitHandle ' Get accessor implementation goes here. End Property ' Caller can use this to wait until operation is complete. } La interfaz utilizada como parámetro del delegado en el siguiente ejemplo de código se define en la biblioteca de clases de . ReadOnly Property AsyncState() As [Object] ' Get accessor implementation goes here.

el servidor no puede utilizar ninguno de los recursos proporcionados por el cliente fuera de la semántica de uso compartido acordada. Object AsyncState { get. una vez finalizado el tiempo de espera seleccionado. una vez que la propiedad IsCompleted devuelve el valor true. bool IsCompleted { get. Es decir. Object AsyncObject { get. } } Observe que el objeto que implementa IAsyncResult (Interfaz) debe ser un objeto temporizador de espera cuya primitiva de sincronización subyacente debe marcarse una vez cancelada o realizada la llamada. bool CompletedSynchronously { get. La propiedad IsCanceled se establecerá en true si se cancela la llamada. public interface IAsyncResult { // Returns true if the asynchronous operation has completed. El tiempo de ejecución proporciona un número de objetos temporizador de espera que reflejan las primitivas de sincronización de Win32. Una vez que el servidor establece la propiedad IsCompleted como true. y la propiedad IsCompleted se establecerá en true después de que el servidor complete el procesamiento de la llamada.End Interface [C#] public delegate AsyncCallback (IAsyncResult ar). } // The delegate object for which the async call was invoked. Observe que sólo se recomienda una solicitud del cliente y del servidor para tenerlo en cuenta. ya que se pueden estar utilizando en el servidor. Además. También proporciona métodos compatibles con la espera para que los objetos de sincronización se marquen con la semántica "any" o "all". WaitHandle AsyncWaitHandle { get. una vez recibida la notificación de cancelación del método. como ManualResetEvent. es seguro para el cliente destruir los recursos. el cliente no debe asumir que el servidor ha detenido la solicitud de procesamiento totalmente. . El método Cancel es una solicitud para cancelar el procesamiento del método. en vez de hacer un sondeo. De este modo. AutoResetEvent y Mutex. Esto permite al cliente esperar a que se complete la llamada. } // Returns true if the call completed synchronously. Estos métodos reconocen el contexto para evitar bloqueos. se recomienda al cliente no destruir recursos como objetos file. } // Caller can use this to wait until operation is complete. } // The state object passed in through BeginInvoke.

. public ProcessFactorizeNumber(long number) { _ulNumber = number. _ulNumber. public void FactorizeNumber1() { // Client code. ref factor2. [C#] public class ProcessFactorizeNumber { private long _ulNumber. Console. factor1. En el siguiente código de ejemplo se muestra el modelo de programación en el cliente para invocar al método Factorize de forma asincrónica. // Extract the delegate from the AsynchResult. } [OneWayAttribute()] public void FactorizedResults(IAsyncResult ar) { long factor1=0. factor2). ar). } } // Async Variation 1. // Output the results. // Obtain the result. fd.FactorizedResults callback function // is called when the call completes. factor2=0. // The ProcessFactorizeNumber.La propiedad Server devuelve el objeto de servidor proporcionado por IAsyncResult.EndInvoke(ref factor1.AsyncDelegate.Writeline("On CallBack: Factors of {0} : {1} {2}". FactorizingAsyncDelegate fd = (FactorizingAsyncDelegate) ((AsyncResult)ar).

// Note: If you have pure out parameters. // Waits for the result. FactorizingAsyncDelegate fd = new FactorizingAsyncDelegate (pf.BeginInvoke(factorizableNum. state). temp=0. you do not need // the temp variable. // Proceed to do other useful work. ProcessFactorizedNumber fc = new ProcessFactorizedNumber(factorizableNum). // Define the AsyncCallback delegate. AsyncCallbackDelegate cb = new AsyncCallback(fc. ref temp. // Asynchronously invoke the Factorize method on pf. // Async Variation 2. ref temp. PrimeFactorizer pf = new PrimeFactorizer(). // Any object can be the state object.PrimeFactorizer pf = new PrimeFactorizer(). // Note: If you have pure out parameters.Factorize). // Create an instance of the class // to be called when the call completes. // Asynchronously invoke the Factorize method on pf. // Create an instance of the class that // will be called when the call completes. public void FactorizeNumber2() { // Client code. FactorizingAsyncDelegate fd = new FactorizingAsyncDelegate (pf. cb. you do not need the // temp variable.Factorize). IAsyncResult ar = fd. temp=0. Object state = new Object(). . long factorizableNum = 1000589023. long factorizableNum = 1000589023.FactorizedResults).

null. if(ar. // Asynchronously invoke the Factorize method on pf. IAsyncResult ar = fd. factor2=0. // Define the AsyncCallback delegate. // Output the results.WaitOne(10000. y el llamador no puede suponer nada sobre la finalización de este tipo de llamada cuando se devuelve el control de ejecución.Writeline("Sequential : Factors of {0} : {1} {2}". si se llama a EndInvoke antes de completar la operación asincrónica. la función de devolución de llamada se enviará a través de la infraestructura del distribuidor de contextos.FactorizedResults). . Console. factorizableNum. ref temp. ar.ProcessFactorizedNumber fc = new ProcessFactorizedNumber(factorizableNum). AsyncCallback cb = new AsyncCallback(fc. Además. } } Observe que si FactorizeCallback es una clase enlazada a un contexto que requiere un contexto sincronizado o de afinidad de subprocesos. Es decir. Object state = new Object(). factor1.BeginInvoke(factorizableNum. Llamar una segunda vez con igual AsyncResult queda sin definir.IsCompleted) { int factor1=0. Ésta es la semántica de un calificador unidireccional de firmas de métodos. se bloqueará el llamador. ref temp. // Obtain the result. la función de devolución de llamada se puede ejecutar de forma asincrónica con respecto al llamador de estos contextos. ar). factor2). null).EndInvoke(ref factor1. // Any object can be the state object.AsyncWaitHandle. ref factor2. fd. Estas llamadas al método se pueden ejecutar de forma sincrónica o asincrónica con respecto al llamador. false).

NumToRead As Long. Espere en el objeto IAsyncResult. NumToWrite As Long) As Long ' Asynchrnous write method. Function Read(buffer() As [Byte]. la primera parte también acepta un objeto AsyncCallbackDelegate que se llama después de completar la operación asincrónica. Un escenario en el que los métodos de lectura y escritura asincrónico y sincrónico son convenientes es el uso de la entrada/salida de archivos. . En el ejemplo siguiente se ilustra el modelo de diseño que muestra cómo el objeto File implementa las operaciones de lectura y escritura. Normalmente. que estará bloqueada hasta que la operación se complete. ' Synchronous read method. el cliente puede suministrar el delegado de la función de devolución de llamada. Function Write(buffer() As [Byte]. Además de la entrada necesaria para la operación asincrónica. Intente completar la operación prematuramente. Las siguientes opciones están disponibles en el cliente para completar las operaciones asincrónicas: • • • • Sondee el objeto IAsyncResult devuelto para su finalización. La diferencia entre esta opción y la opción anterior es que se pueden utilizar tiempos de espera periódicamente para volver a tomar el control. La primera parte devuelve un objeto temporizador de espera que implementa la interfaz IAsyncResult utilizada por el cliente para determinar el estado de la operación asincrónica. NumToRead As Long) As Long ' Asynchronous read method.Resumen del modelo de diseño para programación asincrónica El servidor divide una operación asincrónica en dos partes lógicas: la parte que acepta la entrada del cliente e inicia la operación asincrónica y la parte que proporciona los resultados de la operación asincrónica para el cliente. o bien no suministrarlo. Complete la operación dentro de la rutina de la función de devolución de llamada. El cliente utiliza la segunda parte para obtener los resultados de la operación asincrónica suministrando el objeto temporizador de espera. Cuando se inician las operaciones asincrónicas. cb As AsyncCallbackDelegate) As IAsyncResult Function EndRead(ar As IAsyncResult) As Long ' Synchronous write method. Function BeginRead(buffer() As [Byte]. el servidor también utiliza el objeto temporizador de espera que ha devuelto al cliente para mantener cualquier estado asociado con la operación asincrónica. [Visual Basic] Public Class File ' Other methods for this class go here.

// Asynchronous read method.Function BeginWrite(buffer() As [Byte]. long NumToWrite. IAsyncResult BeginWrite(Byte[] buffer. long NumToRead. cb As AsyncCallbackDelegate) As IAsyncResult Function EndWrite(ar As IAsyncResult) As Long End Class [C#] public class File { // Other methods for this class go here. NumToWrite As Long. adopten un parámetro del objeto adicional que represente el estado y que se captura en la interfaz IAsyncResult. // Synchronous read method. AsyncCallbackDelegate cb). // Asynchrnous write method. Esto se puede solucionar haciendo que los métodos Begin. IAsyncResult BeginRead(Byte[] buffer. long EndWrite(IAsyncResult ar). long NumToWrite). long Write(Byte[] buffer. // Synchronous write method. long NumToRead). long Read(Byte[] buffer. AsyncCallbackDelegate cb). long EndRead(IAsyncResult ar). como BeginWrite. } El cliente no puede asociar fácilmente el estado con una operación asincrónica dada sin definir un nuevo delegado de la función de devolución de llamada para cada operación. .