Herramientas de usuario

Herramientas del sitio


highlevel:csharp:xtra

¡Esta es una revisión vieja del documento!


Xtra

Aplicaciones

Acceso al registro de Windows

Para acceder al registro de Windows no debemos olvidar incluir con un using el espacio de nombres Microsoft.Win32. También necesitaremos un objeto RegistryKey.
Para acceder al registro debemos ver este como si fuese un objeto ya inicializado, éste objeto .NET lo llama Registry. Sus principales ‘métodos’ son las ramas principales del registro: P.ej: LocalMachine, CurrentUser, CurrentConfig…

  • Acciones:
    • Y para acceder más arriba deberemos asignar al objeto RegistryKey el valor devuelto por: .OpenSubKey (<Path de donde querámos ir>).
    • Para saber cuantas subramas tiene donde tamos: .SubKeyCount
    • Para saber los valores que tiene la rama donde tamos: .Valuecount
    • Para cerrar la clave donde estamos y que los cambios realizados se guarden: Close
  • Leer claves del registro:
    • Para recoger un valor: GetValue (<Y el elemento que queramos pillar>), esto devolverá un objeto ‘object’.
RegistryKey Reg = Registry.LocalMachine;
Reg = Reg.OpenSubKey 			("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
object Speed = Reg.GetValue("~MHz");
  • Métodos sobre escribir en el registro
    • CreateSubKey: Crea (o abre) una subclave
    • DeleteSubKey: Elimina una subclave
    • DeleteSubKeyTree: Elimina una clave y todas las subclaves
    • GetSubKeyNames: Devuelve una matriz de strings con todos los nombres de las subclaves
    • GetValueNames: Devuelve una matriz de strings con los valores de la clave.
    • SetValue: Asigna un valor clave.

Arrastrar y soltar (Drag Drop)

En las listas, en los formularios y en distintos tipos de objetos puede implementarse el Drag and Drop, esto es, arrastrar desde un objeto a otro y lo que ocurre cuando esto ocurre. Implementar este efecto no es muy complejo:

  1. Se activa la acción de arrastrar en el objeto inicial al clicar sobre sus datos y se mantendrá hasta que se suelte el botón del ratón.
  2. Al recibir datos arrastrados debemos coger dichos datos y adecuarlos al valor del objeto destino.

Para que un objeto pueda recibir datos, éste debe de tener la propiedad boleana AllowDrop a true, a partir de entonces, todo objeto que llegue por arrastre será gestionado.
En el método de hacer “mouse down” sobre el objeto codificaríamos lo que vamos a enviar al otro objeto.
Cuando entre un Drag (DragEnter) en el objeto destino debemos codificar mediante los DragDropEffects como actuará dicho objeto.
Una vez se ha levantado el botón del ratón sobre el objeto destino se ha acabado de hacer la acción DragDrop, esta se ha efectuado sobre el objeto destino por lo que será él quien llame al método. Éste método recibe un parámetro DragEventArgs, que aquí es donde vienen todos los datos que se nos han pasado, para recogerlos debemos llamar al método .Data.GetData() mandándole como parámetro un valor de la enumeración DataFormats (un texto, un fichero…).

El siguiente ejemplo corresponde a un formulario con dos listas y un textbox. Se podrán intercambiar datos entre las dos listas y en el textbox se escribirán las direcciones de los ficheros que le han sido pasados al programa.

Evento MouseDown de un listbox

Antes de empezar el arrastre pongo las propiedades del propio listBox y del formulario a false, para que, si se hace doble clic no se vuelvan a copiar sobre la lista o si se deja en el formulario no haya ningún error raro por culpa del textBox. Hace que en el objeto lista orígen se inicie el DragDrop mediante la función DoDragDrop, a esta se le pasan dos parámetros: lo que se arrastra y como se arrastra (mediante la enumeración DragDropEffects). Para borrarlo de la lista nos hemos asegurado que al pasar lo devuelto por esta función a string no devuelva un None, lo que devuelve es el como ha recibido el objeto destino el DragDrop. Entonces borro el elemento que hemos cogido y vuelvo a dejarlo todo como estaba.

private void listBox1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) {
	int idx = listBox1.SelectedIndex;
	if (idx >= 0) {
		listBox1.AllowDrop = false;
		this.AllowDrop = false;
		if (listBox1.DoDragDrop(listBox1.Items[idx].ToString(), DragDropEffects.Copy).ToString() != "None")
			listBox1.Items.RemoveAt(idx);
		listBox1.AllowDrop = true;
		this.AllowDrop = true;
	}
}

Evento DragEnter en un listbox

Lo único que hemos hecho es poner el efecto de DragEventArgs a uno que sea viable para el traspaso de datos.

private void listBox2_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) {
	e.Effect = DragDropEffects.Copy;
}

Evento DragDrop en un listBox

En este evento se reciben dos parámetros, el objeto que lo ha realizado y los datos enviados, si de estos datos cogemos sus datos (según el tipo de datos los cogeremos de una forma u otra) ya podemos añadirlo a la lista.

private void listBox2_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) {
	listBox2.Items.Add(e.Data.GetData(DataFormats.StringFormat));
}
private void listBox1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) {
	e.Effect = DragDropEffects.Copy;
}
private void listBox1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) {
	listBox1.Items.Add(e.Data.GetData(DataFormats.StringFormat));
}
private void listBox2_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) {
	int idx = listBox2.SelectedIndex;
	if (idx >= 0) {
		listBox2.AllowDrop = false;
		this.AllowDrop = false;
		if (listBox2.DoDragDrop(listBox2.Items[idx].ToString(), DragDropEffects.Copy).ToString() != "None")
			listBox2.Items.RemoveAt(idx);
		listBox2.AllowDrop = true;
		this.AllowDrop = true;
	}
}

Respecto al recibir los datos en el formulario

Cuando se arrastra un fichero a un formulario (o objeto de formulario) ya no es un “texto” como antes indicábamos con la enumeración del GetData, sino un FileDrop, esto es, un array de strings que tendrá el mismo número de elementos que archivos se hayan arrastrado, cada elemento es una dirección de archivo.

private void Form1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) {
	e.Effect = DragDropEffects.Copy;
}
private void Form1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) {
	string[] FileProp;
	FileProp = (string[])e.Data.GetData(DataFormats.FileDrop);
	foreach (string str in FileProp)
		textBox1.Text += str + " ";
}

General

Fechas

La clase DateTime nos permite la manipulación de fecha y hora de una forma muy sencilla, contiene fechas y horas. Para la manipulación de estas podemos usar métodos como ToShortDateString, ToShortTimeString, ToLongDateString… que te devuelven strings con la fecha u hora, según se haya seleccionado, del objeto.

  • También podremos sumar y restar fechas (u horas) con los operadores correspondientes.
  • Métodos públicos como Add, AddHours, AddDays… nos permiten añadir horas, días… al objeto DateTime.
  • Date, Day, Hour, DayOfWeek, DayOfYear, Month, Second, Millisecond… Son propiedades que nos devuelven en números enteros el día, la hora…
  • El método Now nos devolverá el día y la hora actual en el PC. O Today la fecha actual.

TimeSpan

El TimeSpan es otra forma de representar el tiempo. Tiene varios constructores, el más completo es al que se le pasa cinco parámetros enteros. El primero representa los días, el segundo las horas, tercero minutos, cuarto segundos y quinto milisegundos. Un objeto:

TimeSpan delay = new TimeSpan(0,0,0,10,0);

Contendría 10 segundos. Luego podríamos manipularlo de forma muy fácil.

Escribir XML

Escribir XML desde C# es fácil, .NET tiene un objeto llamado XmlTextWriter, para acceder a él hemos de hacer referencia a System.Xml (tanto dentro de references como con using).
Para hacer uso de él debemos de saber:

  • Su constructor está sobrecargado, podemos llamarlo con dos parámetros: con un string, la ruta del archivo donde se escribirá, y con null, para que no nos haga codificación del texto (como en el ejemplo), o con un solo parámetro, que sería por donde sale el texto:
XmlTextWriter XMLW = new XmlTextWriter(Console.Out);
  • Podemos formatear la salida indicando en la propiedad Formatting la enumeración Formatting.Indented.
  • Escribir un comentario llamando al método WriteComment, que espera un string.
  • Para empezar a escribir un elemento llamaremos a WriteStartElement y el nombre del elemento en un string, y para acabarlo llamando a EndElement.
  • Para introducir una propiedad llamaríamos al método WriteElementString, el nombre de la propiedad, y el valor con dos strings.
  • Cerrarlo mediante los métodos WriteEndDocument y Close.
XmlTextWriter XMLW = new XmlTextWriter(Path, null);
XMLW.Formatting = Formatting.Indented;
XMLW.WriteStartDocument();
XMLW.WriteComment("Vamos a ver qué sale");
XMLW.WriteStartElement("Libro");
XMLW.WriteElementString("Titulo", "1984");
XMLW.WriteElementString("Autor", "G.Orwell");
XMLW.WriteEndElement();
XMLW.WriteEndDocument();
XMLW.Close();

Acceder a los valores de los atributos

Tenemos este atributo:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)]
class HelpAttribute : System.Attribute {
	protected string Descripcion;
	public string Uso;
	public HelpAttribute (string Explicacion) {
		this.Descripcion = Explicacion;
		this.Uso = "nulo";
	}
	public string Description {
		get {
			return this.Descripcion;
		}
	}
}

Y lo vamos a asignar al ensamblado, a una clase y a un método. Cómo podemos acceder a él para recoger sus valores? Lo haremos de las formas siguientes:

  • Coger los atributos del propio ensamblado: Cogemos el nombre del ejecutable (Proceso actual + .exe), miraremos el valor de un atributo cada vez que se ha instanciado en ese ensamblado, cargando como un Assembly (dentro de System.Reflection) y este recogiendolo como un HelpAttribute: <variable HelpAttribute> = <variable Attribute> as HelpAttribute;
static void MiraAtrPropio() {
	HelpAttribute HelpAtr;
	string AsmName;
	Process p = Process.GetCurrentProcess();
	AsmName = p.ProcessName + ".exe";
 
	Assembly a = Assembly.LoadFrom(AsmName);
	foreach (Attribute atr in a.GetCustomAttributes(true)) {
		HelpAtr = atr as HelpAttribute;
		if (HelpAtr != null)
			Console.WriteLine("\t {0}",HelpAtr.Description);
	}
}
  • Coger los atributos de las clases, de los métodos y hasta de los campos del ensamblado:
    • Definiremos una variable type del tipo de la clase que queramos ver los atributos.
    • Definiremos una variable temporal HelpAttribute.
    • Por cada atributo (que leeremos como HelpAttribute) de la varable type (que sabremos mediante GetCustomAttributes) los mostraremos por pantalla su descripción.
    • Para coger los métodos de una clase llamaremos a GetMethods y los meteremos en una variable MethodInfo, los atributos de esta (con GetCustomAttributes).
    • Para los campos igual que con los métodos paro Cogiendo los campos con GetFields metiendolos en una variable del tipo FieldInfo.
Type type = typeof(Class1);
HelpAttribute HelpAtr;
foreach (Attribute atr in type.GetCustomAttributes(true)) {
	HelpAtr = atr as HelpAttribute;
	if (HelpAtr != null)
		Console.WriteLine("\t {0}", HelpAtr.Description);
}
foreach (MethodInfo metodo in type.GetMethods()) {
	foreach (Attribute atr in metodo.GetCustomAttributes(true)) {
		HelpAtr = atr as HelpAttribute;
		if (HelpAtr != null) {
			Console.WriteLine("\t\t Método: {0}", metodo.ToString());
			Console.WriteLine("\t\t\t {0}", HelpAtr.Description);
		}
	}
}

Manipulación de archivos

La manipulación de archivos se basa en la copia, movimiento y borrado del fichero. Para poder hacer esto existe una clase dentro de System.IO llamada FileInfo. Al crear un objeto de este tipo debemos pasarle al constructor el nombre del archivo principal.

Copiar archivos

Un objeto FileInfo tiene un método llamado CopyTo, este método recibe como parámetros el nombre del segundo archivo y un boleano con un true si se puede copiar o un false sino.

FileInfo fi = new FileInfo("c:\\burro.txt");
fi.CopyTo("c:\\Hulk\\holass.txt",true);

Borrar archivos

Para borrar un archivo, sólo tenemos que llamar al método .Delete() del objeto FileInfo. Con este también podemos borrar directorios.

FileInfo fi = new FileInfo("c:\\Hulk\\holass.txt");
fi.Delete();

Mover archivos

Método MoveTo y el parámetro es el destino.

Propiedades de los archivos

Recuerda, las propiedades y métodos han de ser pasadas a string para que puedan leerse.
Además podemos saber cuando se crearon mediante el método: .CreationTime().
O el directorio donde está con .Directory o .DirectoryName.
Con la propiedad bool .Exists podemos saber si el fichero existe.
La propiedad int .Length nos dará su tamaño.
FullPath o OriginalPath pueden ser usados para saber el path del archivo.
.Extension nos dará la extensión del archivo.
Y Attributes sus atributos.

Flujos de datos

Al guardar sólo bytes en el archivo debemos llamar al método Write del FileStream, éste espera un array de bytes que será lo que meta en el fichero y dos parámetros más, en qué posición del array empezará a meter datos y hasta qué posición.

#region Flujo de bytes
class ByteStream  {
	private byte [] ArrayBytes;
	private byte [] ArrayLeido;
	private FileStream Fichero;
	public ByteStream (string path) {
		ArrayBytes = new byte[255];
		ArrayLeido = new byte[255];
		Fichero = new FileStream(path, FileMode.Create, FileAccess.ReadWrite);
	}
	public void Write () {
		for (int i = 0; i < 255; i++)
			ArrayBytes[i] = (byte)i;
		Fichero.Write(ArrayBytes,0,255);
	}
	public void Read () {
		Fichero.Seek(0, SeekOrigin.Begin);
		Fichero.Read(ArrayLeido,0,255);
		for (int i=0; i < 255; i++)
			Console.WriteLine(ArrayLeido[i]);
	}
}
#endregion

Al guardar datos en formato binario escribimos directamente el dato como si fuese el parámetro. Y luego al leer del archivo, debemos saber que tamos leyendo, ya que al BinaryReader se llama al método que sea según lo que deba leer.

#region Flujo Binario
class BinaryFlux {
	private FileStream Fichero;
	public enum Modo {Leer, Escribir};
	public BinaryFlux (string path, Modo mode) {
		if (mode == Modo.Escribir)
			Fichero = new FileStream(path, FileMode.Create, FileAccess.Write);
		if (mode == Modo.Leer)
			Fichero = new FileStream(path, FileMode.Open, FileAccess.Read);
	}
	public void Write () {
		BinaryWriter Writer = new BinaryWriter(Fichero);
		Writer.Write('a');
		Writer.Write(123);
		Writer.Write(343.43f);
		Writer.Write("probando");
		Writer.Write((byte)5);
		Writer.Close();
		Fichero.Close();
	}
	public void Read () {
		BinaryReader Reader = new BinaryReader(Fichero);
		Fichero.Seek(0,SeekOrigin.Begin);
		Console.WriteLine(Reader.ReadChar());
		Console.WriteLine(Reader.ReadInt32());
		Console.WriteLine(Reader.ReadSingle());
		Console.WriteLine(Reader.ReadString());
		Console.WriteLine(Reader.ReadByte());
		Reader.Close();
		Fichero.Close();
	}
}
# endregion

Ejemplo de la clase StreamWriter:

StreamWriter writer = File.CreateText(this.txtHDDir.Text);
if (this.txtSince.Text != "")
	iDesde = System.Int32.Parse(this.txtSince.Text);
else
	iDesde = 0;
if (this.txtTo.Text != "")
	iHasta = System.Int32.Parse(this.txtTo.Text);
else
	iHasta = 0;
for (int i = iDesde; i <=iHasta; i++) {
	strDirTotal = this.txtIniDir.Text + i.ToString() + this.txtExt.Text;
	foreach (char chrTemp in strDirTotal)
		writer.Write(chrTemp);
	writer.Write(writer.NewLine);
}

El framework de .NET

Definiciones

  • Lenguaje MSIL: Lenguaje intermedio de Microsoft, todas las compilaciones de .NET se crean en este lenguaje.
  • CLR: Entorno de ejecución, mediante este el framework gestiona las aplicaciones.
  • GAC (Caché de Ensamblados Global): Carpeta donde se guardan los ensamblados globales, a los que podemos hacer referencia desde cualquier proyecto .NET, la encontraremos en: <carpeta de sistema>/assembly
  • JIT: Compilador usado para leer ensamblados de forma óptima.

Aplicaciones del Framework

Estas se encuentran en el directorio donde se ha instalado .NET/FrameworkSDK/Bin o en la carpeta de sistema /Microsoft.NET/Framework/<versión>.

Herramientas de configuración e implementación

  • Visor de la caché de ensamblados (Shfusion.dll): Permite ver y manipular el contenido de la caché de ensamblados global utilizando Windows Explorer.
  • Assembly Linker (Al.exe): Genera un archivo con un manifiesto del ensamblado a partir de uno o varios archivos que son archivos de recursos o archivos de Lenguaje intermedio de Microsoft (MSIL).
  • Herramienta Registro de ensamblado (Regasm.exe): Lee los metadatos de un ensamblado y agrega las entradas necesarias al Registro, lo que permite que los clientes COM creen clases de .NET Framework de forma transparente.
  • Visor de registro de enlaces de ensamblados (Fuslogvw.exe): Muestra los detalles de enlaces de ensamblado erróneos. Esta información le ayuda a diagnosticar por qué .NET Framework no encuentra un ensamblado en tiempo de ejecución.
  • Herramienta Caché de ensamblados global (Gacutil.exe): Permite ver y manipular el contenido de la caché de ensamblados global y de la caché de descarga. Si bien Shfusion.dll proporciona una funcionalidad similar, puede utilizar Gacutil.exe desde secuencias de comandos de generación, archivos MAKE y archivos por lotes.
  • Herramienta Installer (Installutil.exe): Permite instalar y desinstalar recursos de servidor ejecutando los componentes del instalador de un ensamblado especificado.
  • Herramienta Almacenamiento aislado (Storeadm.exe): Muestra o quita todos los almacenamientos existentes del usuario conectado actualmente.
  • Herramienta Generador de imágenes nativas (Ngen.exe): Crea una imagen nativa desde un ensamblado administrado y la instala en la caché de imágenes nativas del equipo local.
  • Herramienta Configuración de .NET Framework (Mscorcfg.msc): Proporciona una interfaz gráfica para administrar la directiva de seguridad de .NET Framework y las aplicaciones que utilizan servicios remotos. Esta herramienta también permite administrar y configurar ensamblados en la caché de ensamblados global.
  • Herramienta de instalación de .NET Services (Regsvcs.exe): Agrega clases administradas a los Servicios de componentes de Windows 2000 cargando y registrando el ensamblado y generando, registrando e instalando la biblioteca de tipos en una aplicación COM+ 1.0 existente.
  • Herramienta Soapsuds (Soapsuds.exe): Le ayuda a compilar aplicaciones cliente que se comunican con servicios Web XML mediante una técnica denominada remota.
  • Exportador de la biblioteca de tipos (Tlbexp.exe): Genera una biblioteca de tipos desde un ensamblado de Common Language Runtime.
  • Importador de la biblioteca de tipos (Tlbimp.exe): Convierte las definiciones de tipo que se encuentran en una biblioteca de tipos COM en definiciones equivalentes en formato de metadatos administrados.
  • Herramienta Lenguaje de descripción de servicios Web (Wsdl.exe): Genera código para los servicios Web XML y los clientes de servicios Web XML a partir de archivos de contrato de Lenguaje de descripción de servicios Web (WSDL), archivos de Definición de esquemas XML (XSD) y documentos de descubrimiento .discomap.
  • Herramienta Descubrimiento de servicios Web (Disco.exe): Descubre las direcciones URL de los servicio Web XML ubicados en un servidor Web y guarda los documentos relacionados con cada servicio Web XML en un disco local.
  • Herramienta Definición de esquemas XML (Xsd.exe): Genera esquemas XML de acuerdo con el lenguaje de definición de esquemas XML (XSD) propuesto por el consorcio World Wide Web (W3C). Esta herramienta genera clases Common Language Runtime y clases DataSet a partir de un archivo de esquema XSD.

Herramientas del depurador

  • Microsoft CLR Debugger (DbgCLR.exe): Proporciona servicios de depuración con una interfaz gráfica para ayudar a los programadores de aplicaciones a detectar y corregir errores en programas para el entorno de tiempo de ejecución. Para obtener más información, consulte el tema CLR Debugger en la documentación de .NET Framework SDK.
  • Depurador en tiempo de ejecución (Cordbg.exe): Proporciona servicios de depuración de líneas de comandos utilizando la API de depuración de Common Language Runtime. Utilice esta herramienta para detectar y corregir errores en programas para el entorno de tiempo de ejecución.

Herramientas de seguridad

  • Herramienta de creación de certificados (Makecert.exe): Genera certificados X.509 únicamente con fines de prueba.
  • Herramienta de administración de certificados (Certmgr.exe): Administra los certificados, las listas de certificados de confianza (CTL) y las listas de revocación de certificados (CRLs).
  • Herramienta de comprobación de certificados (Chktrust.exe): Comprueba la validez de un archivo firmado con un certificado X.509.
  • Herramienta de la directiva de seguridad de acceso al código (Caspol.exe): Permite examinar y modificar las directivas de seguridad de acceso al código del equipo, del usuario y de la empresa.
  • Herramienta de firma de archivos (Signcode.exe): Firma un archivo ejecutable portable (PE) con una firma digital Authenticode.
  • Herramienta de vista de permisos (Permview.exe): Muestra los conjuntos de permisos mínimos, opcionales y rechazados requeridos por un ensamblado. También puede utilizar esta herramienta para ver toda la seguridad declarativa utilizada por un ensamblado.
  • Herramienta PEVerify (PEverify.exe): Realiza comprobaciones de seguridad de tipo de MSIL y comprobaciones de validación de metadatos en un ensamblado especificado.
  • Herramienta Secutil (Secutil.exe): Extrae información de clave pública con nombre seguro o certificados Authenticode de compañía de software a partir de un ensamblado en un formato que se puede incorporar al código.
  • Herramienta Establecer Registro (Setreg.exe): Permite cambiar la configuración de Registro de las claves del estado de publicación de software, que controlan el comportamiento del proceso de comprobación de certificados.
  • Herramienta de prueba de certificados de compañía de software (Cert2spc.exe): Crea, con fines de prueba exclusivamente, un certificado de compañía de software (SPC) a partir de uno o varios certificados X.509.
  • Herramienta de nombre seguro (Sn.exe): Ayuda a crear ensamblados con nombre seguros. Sn.exe proporciona opciones para la administración de claves, la generación y la comprobación de firmas.

Herramientas generales

  • Herramienta de minivolcados de Common Language Runtime (Mscordmp.exe): Crea un archivo con información útil para analizar problemas del sistema en tiempo de ejecución. La herramienta Microsoft Dr. Watson (Drwatson.exe) llama a este programa de forma automática.
  • Compilador de licencias (Lc.exe): Lee los archivos de texto que contienen información sobre licencias y genera un archivo .licenses que se puede incrustar en un archivo ejecutable de Common Language Runtime.
  • Generador de clases con establecimiento inflexible de tipos para administración (Mgmtclassgen.exe): Permite generar rápidamente una clase Early Bound en C#, Visual Basic o JScript para una clase Windows Management Instrumentation (WMI) específica.
  • Ensamblador MSIL (Ilasm.exe): Genera un archivo PE a partir del lenguaje intermedio de Microsoft (MSIL). El archivo ejecutable que se obtiene como resultado, que contiene código MSIL y los metadatos requeridos, se puede ejecutar para determinar si el código MSIL funciona de la forma esperada.
  • Desensamblador MSIL (Ildasm.exe): Utiliza un archivo PE que contiene código MSIL para crear un archivo de texto adecuado como entrada al ensamblador MSIL (Ilasm.exe).
  • Herramienta Generador de archivos de recursos (Resgen.exe): Convierte los archivos de texto y los archivos .resx (formato de recurso basado en XML) en archivos .resources binarios de Common Language Runtime de .NET, que se pueden incrustar en un archivo ejecutable binario en tiempo de ejecución o compilar en ensamblados satélite.
  • Importador de controles ActiveX de Windows Forms (Aximp.exe): Convierte las definiciones de tipo en una biblioteca de tipos COM para un control ActiveX de un control de Windows Forms.
  • Visor de clases de Windows Forms (Wincv.exe): Busca clases administradas que coincidan con un modelo de búsqueda especificado y muestra información sobre las mismas mediante la API de reflexión.
  • Editor de recursos de Windows Forms (Winres.exe): Permite adaptar de forma rápida y sencilla los formularios Windows Forms.

Ensamblados

Los ensamblados son el código compilado por .NET, ya sean librerías o ejecutables. Al iniciar una aplicación se cargan los ensamblados de los que esta depende, a estos se les llama ensamblados referenciados.

Para ver todos los ensamblados referenciados que están ahora cargados desde una aplicación de C# deberemos usar System.Reflection y así, poder acceder a la clase Assembly.
Lo primero que haremos será crear un objeto Assembly e igualarlo al objeto que te devuelva el método GetEntryAssembly(), este devolverá el ensamblado de entrada, éste es el ensamblado que se ejecuta en primer lugar, y el punto de entrada de un ensamblado sería la función que se ejecuta primero (generalmente Main).

Un objeto AssemblyName contiene toda la información de un ensamblado. Si cogemos el ensamblado de entrada y llamamos a su método GetReferencedAssemblies() nos devolverá todos los AssemblyName a los que hace referencia:

Assembly EnsambladoDeEntrada;
EnsambladoDeEntrada = Assembly.GetEntryAssembly();
foreach (AssemblyName NombrEnsamblado in EnsambladoDeEntrada .GetReferencedAssemblies())
Console.WriteLine("Ensamblado: {0}", NombrEnsamblado.ToString());
Console.ReadLine();

Para saber qué entradas tiene el ensamblado .NET mira los manifiestos de este. Para que nosotros podamos ver este “manifiesto” usaremos la herramienta ILDASM.

Un nombre seguro en un ensamblado indica que este puede almacenarse en la GAC, para saber si un ensamblado lo tiene éste ha de tener un nombre, una versión, una referencia cultural y una clave pública (todos tienen todos estos atributos excepto la clave pública). Para dar un ensamblado una clave pública primero deberemos crearla con la herramienta sn del framework. Haremos ‘sn –k <nombre de clave>.snk’, se generará una clave en un archivo snk y para incluirla dentro de nuestro ensamblado una de sus propiedades será:

[assembly: AssemblyKeyFile (<nombre de clave>)]

Una imágen nativa de un ensamblado es cuando el ensamblado se ejecuta de forma óptima para el procesador, para poder hacer que un ensamblado se ejecute en nuestra máquina local de forma nativa deberemos usar el comando ‘ngen <nombre>’.

En C# podemos acceder a la información de un ensamblado muy fácilmente. Empecemos con el ensamblado de entrada, lo recogemos y para saber sus propiedades usaremos los siguientes métodos:

  • .Location: Path del archivo.
  • .GlobalAssemblyCache: Si se ha cargado del GAC
  • .EntryPoint: Punto de entrada, si es null será una .dll
Assembly EnsambladoDeEntrada;
EnsambladoDeEntrada = Assembly.GetEntryAssembly();
Console.Write(EnsambladoDeEntrada.Location);

Para cargar ensamblados dinámicamente usaremos las funciones Load y LoadWithPartialName, a la primera le pasaremos todo el nombre de ensamblado con su path, en cambio la segunda lo buscará en el directorio de la aplicación y en la GAC.

Assembly CargaASM;
CargaASM = Assembly.LoadWithPartialName("System.Xml");
Console.WriteLine(CargaASM.ToString());

Para trabajar con este ensamblado deberemos mirar que objetos de éste podemos usar, para ello miraremos los objetos con el método GetExportedTypes y luego si al crear una instancia a este objeto (mediante el método CreateInstance(nombre del objeto)) no es nula es que ese objeto puede ser usado:

Assembly CargaASM;
Type[] Tipos;
object NewObject;
CargaASM = Assembly.LoadWithPartialName("System.Xml");
Tipos = CargaASM.GetExportedTypes();
foreach(Type TASM in Tipos) {
	try {
		NewObject = CargaASM.CreateInstance(TASM.ToString());
		if (NewObject != null)
			Console.WriteLine("Con {0} se puede trabajar", TASM.ToString());
		else
			Console.WriteLine("Con {0} NO se puede trabajar", TASM.ToString());
	}
	catch {}
}

Equivalencias de tipos con clases

Las variables pueden ser declaradas como int, byte…. O como System.In32, System.Byte… Para mantener la compatitivilidad con otras aplicaciones .net.

sbyte		System.SByte
byte		System.Byte
short		System.Int16
ushort		System.Uint16
int		System.Int32
uint		System.Uint32
long		System.Int64
ulong		System.Uint64
char		System.Char
float		System.Single
double		System.System.Double
bool		System.Boolean
decimal		System.Decimal

System.Collections

  • BitArray: Array de bits, de bools, que son únicamente true (1) o false (0). Sus métodos permiten realizar operaciones binarias (Add, Or, Not, Xor).
  • Queue: Colección de objetos en el que el primero en entrar es el primero en salir. Puede construirse a partir de un array: Queue queue = new Queue(args); Se introducen y sacan archivos con los métodos Enqueue y Dequeue.Para recorrerlo:
while (queue.Count > 0) {
	current	= (string)queue.Dequeue();
}
  • Stack: Colección de objetos en el que el último en entrar es el primero en salir. Se introducen y sacan miembros con los métodos Push y Pop.
  • HashTable: Se usa para el almacenamiento de objetos (values) con id's (keys).

ArrayList

Un ArrayList la encontramos dentro del espacio de nombres System.Collection, es una lista de objetos dinámica. Sus métodos:

  • Add Agrega un objeto al final.
  • BinarySearch Hace una búsqueda binaria
  • Clear Elimina los elementos del ArrayList
  • Contains Determina si un elemento tá en la lista
  • IndexOf Indica el primer indice de donde se encuentra el elemento indicado.
  • Insert Inserta un nuevo elemento en el indice especificado
  • RemoveAt Quita el objeto del índice especificado
  • Sort Ordena
  • ToArray Devuelve el ArrayList en una nueva matriz.
  • Count Devuelve el número de elementos contenido actualmente en la matriz.

Reflection

Usando System.Reflection tenemos la posivilidad de acceder a objetos sin saber muy bien su función o su tipo. Para ello lo que hacemos es declarar una variable Type e igualarla al “tipo” (type) devuelto por la función estática de esta misma clase .GetType(<nombre de clase>).

Para ver las “cualidades” del tipo devuelto tenemos los siguientes métodos:

  • Name Devuelve el nombre de la clase.
  • UndelyingSystemType El tipo con el que el CLR llama a esta clase
  • IsClass Devuelve true o false según si es una clase o una interfaz.
  • Assembly Donde se encuentra el ensamblado que la usa
  • Attributes Sus atributos
  • Namespace Su espacio de nombres
Type t = Type.GetType("System.String");
Console.WriteLine(t.Name);
Console.WriteLine(t.UnderlyingSystemType);

En vez de usar un nombre de tipo podemos usar una instancia a una clase:

String LoQueSea = "dfjk";
Type t = LoQueSea.GetType();
Console.WriteLine(t.Name);

Para examinar los procesos que se están ejecutando usamos el tipo “process”, para poder hacer uso de esta clase incluiremos System.Diagnostics. Este trozo de código tan intuitivo cierra todos los procesos menos el que provoca el programa que lo contiene.

String EsteProceso;
Process[] p = Process.GetProcesses();
Process ThisProc = Process.GetCurrentProcess();
EsteProceso = ThisProc.ProcessName + ".exe";
foreach (Process proc in p)
	if (proc.ProcessName != EsteProceso)
		proc.Kill();

Con un código parecido a este vamos a examinar las clases de este archivo, aunque podríamos escoger cualquier otro ensamblado:

String EsteProceso;
Process ThisProc = Process.GetCurrentProcess();
EsteProceso = ThisProc.ProcessName + ".exe";
Assembly a = Assembly.LoadFrom(EsteProceso);
Type[] tipo = a.GetTypes();
foreach (Type t in tipo){
	Console.WriteLine (t.FullName);
	Console.WriteLine (t.BaseType.FullName);
}

Y ahora vamos a examinar las propiedades, los métodos y los constructores de una clase. Escogemos una clase, en el ejemplo ClassX, y cogemos su tipo mandándola como parámetro dentro de la función typeof. Una vez tengamos el tipo podemos mirar sus constructores con la función GetConstructors() que recoge un array de ConstructorInfo, o us propiedades con GetProperties que el array devuelto es de PropertyInfo….

Type tipo = typeof(ClassX);
Console.WriteLine (tipo);
ConstructorInfo[] Constructores = tipo.GetConstructors();
foreach(ConstructorInfo i in Constructores)
	Console.WriteLine(i);
PropertyInfo[] Propiedades = tipo.GetProperties();
foreach(PropertyInfo i in Propiedades)
	Console.WriteLine(i);
MethodInfo[] Metodos = tipo.GetMethods();
foreach(MethodInfo i in Metodos)
	Console.WriteLine(i);
MemberInfo[] Miembros = tipo.GetMembers();
foreach(MemberInfo i in Miembros)
	Console.WriteLine("\t"+i);

Código Dinámico

El código de la clase Main llama a una función que crea un ensamblado (dentro del dominio de la aplicación) en tiempo de ejecución, dinámico, dentro de este una clase con sus métodos. Luego se crea un MethodInfo y se le llama. Para crear código dinámico debemos usar System.Reflection.Emit.

class Class1 {
  static Type CodigoDinamico;
  static void Main(string[] args) {
	GeneraCodigo();
	object o = Activator.CreateInstance(CodigoDinamico);
	MethodInfo Funcion = CodigoDinamico.GetMethod("Prueba");
	Funcion.Invoke(o, null);
	Console.ReadLine();
  }
  static void GeneraCodigo() {
	AppDomain DominioAplicacion = AppDomain.CurrentDomain;
	AssemblyName Ensamblado = new AssemblyName();
	Ensamblado.Name = "Prueba";
	AssemblyBuilder ASMConstructor = DominioAplicacion.DefineDynamicAssembly(Ensamblado, AssemblyBuilderAccess.Run);
	ModuleBuilder Modulo = ASMConstructor.DefineDynamicModule("ModuloDelEnsamblado");
	TypeBuilder ConstrucTipo = Modulo.DefineType("ClaseDinamica", TypeAttributes.Public);
	MethodBuilder ConstrucMetodos = ConstrucTipo.DefineMethod("Prueba", MethodAttributes.Public, null, null);
	ILGenerator MSILcode = ConstrucMetodos.GetILGenerator();
	MSILcode.EmitWriteLine("Probando... Probando...");
	MSILcode.Emit(OpCodes.Ret);
	CodigoDinamico = ConstrucTipo.CreateType();
  }
}

Threads

Al ejecutarse un programa se crea un proceso principal, si este programa abre “tareas en segundo plano”, estas serán subprocesos. La multitarea preferente se basa en dedicar a cada proceso (o subproceso) del sistema un espacio de tiempo, si en cambio el sistema tiene varios procesadores habá un multiprocesamiento simétrico. Al crearse un subproceso este recibe una prioridad, cuanta más prioridad tiene más atención le dedica el sistema.

Los subprocesos son aconsejables de usar dentro de aplicaciones con procesos largos, que ese subproceso se vaya ejecutando mientras el usuario sigue con el principal. Aplicaciones que hagan varias cosas a la vez, como por ejemplo en la que un proceso escuche por un puerto y otro mande por otro. Hasta el botón cancelar puede ser usado para eliminar un subproceso.

Para definir un subproceso usamos un objeto System.Threading.Thread. Deberemos definir una función para éste, se la pasaremos mediante un ThreadStart, y para iniciarla deberemos llamar al método Start del objeto Thread.

Thread thr1 = new Thread(new ThreadStart(obj.Metodo1));
thr1.Start();

Para ver las propiedades del thread en el que estemos accederemos al Thread.CurrentThread, y las propiedades que podemos ver son: Name (nombre), Priority (prioridad) y ThreadState (estado), los estados son los de la enumeración FlagsAttribute y un Thread puede tener varios estados ya que este puede haber abierto otros subprocesos.

Podríamos ponerle nombre, así cuando debugasemos sería más fácil localizarlo, para ello, antes de llamar a Start podemos hacer (en el ejemplo anterior): thr1.Name = “Thread1”;

También podríamos indicar su prioridad mediante la propiedad Priority con los valores de la enumeración ThreadPriority. P.ej: thr1.Priority = ThreadPriority.Highest;

Hay otros métodos que también podemos usar:

  • Abort Para eliminar el proceso.
  • Suspend Bloquear el subproceso, no continuará hasta que no se haya hecho un resume
  • Resume Desbloquea el subproceso
  • Join A partir de que se llame a este método del thread, el código no continuará hasta que éste no acabe.

Recuerda que podemos acceder al thread en el que estamos haciendo Thread.CurrentThread.Abort (p.ej). Desde el propio Thread también podemos indicar una pausa haciendo Thread.Sleep(milisegundos).

Puede ocurrir que dos procesos ataquen a una sóla función y esto puede hacer errar a dicha función, para que esto no ocurra se bloquea una parte del código de la función. Cuando un proceso entra a un código bloqueado mira si está ocupado, si no lo está lo ocupa y cuando acabe con él lo desocupa, si estubiese ocupado espera hasta que este sea desocupado.

Para bloquear parte del código usaremos: lock(this) { <código bloqueado> }.

class Prueba {
	public void Metodo1 () {
		Metodo3(1);
	}
	public void Metodo2 () {
		Metodo3(2);
	}
	public void Metodo3 (int num) {
		lock(this) {
			Thread.Sleep(1000);
			Console.WriteLine(num);
		}
	}
}
class Class1 {
	static void Main(string[] args) {
		Prueba obj = new Prueba();
		Thread thr1 = new Thread(new ThreadStart(obj.Metodo1));
		thr1.Start();
		Thread thr2 = new Thread(new ThreadStart(obj.Metodo2));
		thr2.Start();
		Console.Read();
	}
}

Acceder a .dll

El acceso en c# a las librerías dll es muy parecido al de VB6, debemos de conocer las funciones de dicha librería, los parámetros que se le pasan y lo que devuelven. Necesitaremos usar la cláusula [DllImport(“<nombre del archivo dll>”)], para poder acceder a esta usaremos System.Runtime.InteropServices. Después del DllImport debemos declarar las funciones que importamos.

using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1 {
	class Class1 {
		[DllImport("jbn000.dll")]	
		public static extern char GetJBNVersion ();
		static void Main(string[] args) {
			System.Console.WriteLine(GetJBNVersion());
			Console.ReadLine();
		}
	}
}

Clase GC

Esta clase hace referencia al recolector de basura de .NET y aunque no aconsejan usarla mucho tiene métodos que nos permite saltarnos la administración automática de memoria del framework para pasar a gestionarla nosotros.

Métodos:

  • Collect Obliga a llevar a cabo la recolección de memoria.
  • KeepAlive Impede que un objeto sea recolectado de forma erronea.
  • SupressFinallize Saca un objeto de la cola de objetos a finalizar.

Estaría bien que en un método Dispose se llamase al método GC.SupressFinallize para que elimine de la cola de objetos Finalizados al objeto ya que no es necesario mantenerlo ahí si este ya ha ejecutado el código de liberación de memoria en Dispose.
También existen métodos para controlar la eliminación de objetos por generaciones (getGenration, Collect, MaxGeneration) que administran los objetos por bloques.

Avanzado

Recursos

Al añadir un archivo a un proyecto este se añade de varias formas (acción de generación):

  • Ninguno: El archivo no está incluido en el grupo de resultados del proyecto, ni se compila en el proceso de generación. Un ejemplo es un archivo de texto que contenga documentación, como un archivo Léame.
  • Compilado: El archivo está compilado en el resultado de la compilación. Este valor se utiliza para los archivos de códigos.
  • Contenido: El archivo no está compilado, pero se incluye en el grupo de resultados del contenido. Por ejemplo, este valor es el predeterminado de un archivo .htm o cualquier otro tipo de archivo Web.
  • Recurso incrustado: Este archivo está incrustado en el resultado de la generación del proyecto principal como un DLL o ejecutable. Este valor se utiliza normalmente para los archivos de recursos.

Como saber qué recursos tiene el ensamblado:

System.Reflection.Assembly thisExe; 
thisExe = System.Reflection.Assembly.GetExecutingAssembly();
string [] resources = thisExe.GetManifestResourceNames();
foreach (string resource in resources)
	MessageBox.Show(resource);

Crear un recurso de cadena de un txt

Un txt con el siguente contenido:

cat001=Hola que tal? Como te va?
cat002=A mi muy bien, y a ti?

Podemos convertirlo en un archivo .resources o .resx (o viceversa) mediante la aplicación de generación de recursos: resgen.

Leer cadenas de archivos de recursos

Siendo el recurso añadido al proyecto: prueba.resources

string assemblyName = Assembly.GetExecutingAssembly().GetName().Name;
string baseName = string.Format( "{0}.prueba", assemblyName );
ResourceManager recurso = new ResourceManager(baseName, Assembly.GetExecutingAssembly());
Console.WriteLine(recurso.GetString("cat01"));

Siendo el recurso añadido al proyecto desde el editor: pruRes.resx

baseName = string.Format( "{0}.pruRes", assemblyName );
recurso = new ResourceManager(baseName, Assembly.GetExecutingAssembly());
Console.WriteLine(recurso.GetString("cas01"));

Coger un archivo imágen añadido al proyecto como recurso

System.Reflection.Assembly thisExe; 
thisExe = System.Reflection.Assembly.GetExecutingAssembly();
string AssemblyName = thisExe.GetName().Name;
System.IO.Stream file = thisExe.GetManifestResourceStream(AssemblyName + ".a.BMP");
this.pictureBox1.Image = Image.FromStream(file);

Cargar una imágen de un archivo externo

this.pictureBox1.Image = Image.FromFile("img.BMP");

Coger recursos de un archivo externo

System.Reflection.Assembly thisExe; 
thisExe = System.Reflection.Assembly.LoadFile("images.dll");
string [] resources = thisExe.GetManifestResourceNames();

Coger archivo XML dentro de recurso

System.IO.Stream fs = Assembly.GetExecutingAssembly().GetManifestResourceStream("YourAssembly.XMLFile1.xml");
DataSet ds = new DataSet(); 
ds.ReadXml(fs);

Servicios COM+

Para crear un servicio COM+ el código se ha de compilar como una librería dinámica y las clases han de heredar de ServicedComponent (dentro de System.EnterpriseServices, recuerda hacer referencia a la dll).
También deberemos indicar mediante el atributo .NET ObjectPooling que la clase ha de agruparse en COM+, éste recibe dos números enteros: el tamaño mínimo y el tamaño máximo.
El CLR no actuará, por lo que si queremos crear un destructor ha de ser del modo tradicional ~NombreClase() { … }.

Para que forme parte de la agrupación de objetos COM+ se debe reemplazar el método virtual CanBePooled() y hacer que devuelva true.

Los métodos virtuales Activate y Deactivate también pueden ser reemplazados. Activate se invoca cuando el objeto se elimina de la agrupación y se asigna a un cliente y Deactivate cuando vuelve a la aplicación. El constructor y el destructor de la clase sólo se invocan una vez, cosa muy distinta a como actúan estos métodos.

Los componentes COM+ deben tener un nombre seguro, que tenga clave pública.

Antes de registrar un componente COM+ podemos ponerlo en la GAG, para registrarlo usaremos la herramienta regsvcs del framework, esta requiere un argumento /appname:<…>, este especifica el nombre del COM+, si al ejecutarlo ya existe un componente con este nombre, las clases de éste se agregarán al que ya existían. Entonces el registro de programa.dll quedará así:

regsvcs /appname:ProgramaCom programa.dll

Y el código de un servicio COM puede ser este:

using System.EnterpriseServices;
[assembly: AssemblyKeyFile("..\\..\\key.snk")]
namespace ClaseCom {
	[ObjectPooling(5,10)]
	public class PowerCom : System.EnterpriseServices.ServicedComponent {
		public PowerCom() {}
		~PowerCom() {}
		public new bool CanBePooled() {
			return true;
		}
		public new void Activate() {}
		public new void Deactivate() {}
	}
}

Y para registrarlo:

regsvcs /appname:prueba clasecom.dll

Anteriormente se usaba el siguiente comando: REGSVR32
Ahora ya es un componente COM+ registrado.

Otros atributos de System.EnterpriseServices son:

  • ApplicationAccessControl Especifica si se configura la seguridad de un ensambado, sus parámetros: true o false.
  • ApplicationActivation Especifica si se agrega la clase a una biblioteca o a una aplicación de servidor, sus parámetros: Librery o Server, es a nivel de ensamblado (esto es que sería algo así: [assembly:ApplicationActivation(Librery)].
  • ApplicationName El nombre de la aplicación COM+, si se especifica este atributo no será necesario usar el parámetro /appname de * regsvcs, es a nivel de ensamblado.
  • AutoComplete No pide parámetros y se aplica a los métodos de la clase COM+, la llamada a los metodos marcados como AutoComplete se ha de hacer seguida de .SetComplete().
  • JustInTimeActivation Habilita o deshabilita la activación JIT, se debe de aplicar a nivel de clase.

System.Management

Desde este espacio de nombres podemos usar el Windows Management (WMI). Esto nos sirve para usar un lenguaje parecido al SQL (WQL) para acceder a las clases del sistema operativo (en la SDK: Operating System Classes o Computer System Hardware Classes), lo que nos proporciona una información muy importante del sistema.

Para acceder a esta tenemos disponibles varios objetos, los ManagementObject. Un ManagementObject es una especie de array de lo que devuelve una consulta WQL. Por ejemplo, en una consulta a Win32_LogicalDisk se devolverá un objeto ManagementObject con las propiedades de esta clase, es decir, nombre (string), size (long), status (string)….
Órdenes WQL válidas pueden ser:

Select * from Win32_LogicalDisk
Select FreeSpace,Size,Name from Win32_LogicalDisk where DriveType=3

Un objeto que lleve un array de ManagementObject es el ManagementObjectCollection, este objeto es lo que realmente devuelve una consulta WQL.

Si quisiesemos acceder a una propiedad de un ManagementObject lo pondríamos entre corchetes, por ejemplo a la propiedad Name de un Win32_LogicalDisk guardado en un objeto ManagementObject llamado MO, sería: MO[“Name”].

Para realizar una consulta WQL lo haremos mediante el objeto ManagementObjectSearcher, que al llamar a su constructor se le pasa un string que sería la WQL. Luego, al llamar al método .Get devolvería ese ManagementObjectCollection del que hemos hablado antes.

Notas

Acceder a los nombres de una enumeración

La base para las enumeraciones está en System.Enum y mediante esta podemos saber los nombres de una enumeración:

enum Meses = {Enero, Febrero, Marzo}
string[] names = System.Enum.GetNames(typeof(Meses));

Herencia en estructuras

  • Las estructuras no heredan de object, sino de ValueType.
  • No pueden heredar de otra cosa que no sea una interfaz.

El por qué del STAThread

Cuando vamos a usar, por ejemplo, el método Clipboard.SetText y no tenemos el STAThread declarado en el método main nos salta un error. Para que nuestro thread pueda acceder a ciertas características del operativo debemos incluir este atributo al main:

[STAThread]
public static void Main() {...}

Constructores que invocan a otros constructores

Cuando una clase hereda de otra, al crear un objeto de esta se llama automáticamente al constructor por defecto de la clase que hereda, a no ser que le sea indicado otro constuctor de la clase base al que llamar.
No sólo se pueden indicar constructores de la clase base, sino otro de la clase a la que ese constructor pertenece, aún así, siempre se llamará al de la clase base, aunque también se llame a otro de esa clase y luego al llamado.
Siempre se llama al constructor de la clase base antes que al del objeto creado, luego llamará al otro indicado y finalmente al que se ha llamado para crear e objeto.

Para hacer estas indicaciones podemos hacer esto:

  • Llama al constructor por defecto de la clase base (si su constructor con int no llama a otro), al suyo con int y luego al suyo por defecto.
public Class1 () : this(12); 
  • Llama al constructor con string de la clase base y luego al por defecto de esta clase.
public Class1() : base("a"); 
  • Llama al constructor con string de la clase base, pasandole la variable a, y luego al de esta, con la variable a también.
public Class1(string a) : base (a)

Difencia entre new metodo y override método

Cuando hacemos un override de un método lo sobreescribimos tanto en la clase base como en la actual, y por mucho que convirtamos el objeto en el que tenemos el override en un objeto del que hereda, el método al que se llama es al del override y para acceder al de la clase base lo podremos hacer desde esa clase indicando base.metodo, pero no desde fuera. Si en la clase base se llama al método virtual, el que tiene el override, desde un objeto heredado, se llamará al método sobreescrito. Al sobreescribir con new únicamente lo hacemos en la clase heredada, luego, convirtiendo el objeto en un tipo de la clase de la que hereda podremos acceder al método de su clase base.

Trucos del Visual Studio

  • Podemos señalar código y ocultarlo mediante la cláusula region. Ponemos #region un nombre y el código y luego #endregion.
  • Desde el modo diseño, para pasar al código podemos hacer F7.

Tools & APIs

  • Herramienta para la generación de código: ndoc.
  • Librería para el uso de ficheros .ini: nini
highlevel/csharp/xtra.1230460038.txt.gz · Última modificación: 2020/05/09 09:24 (editor externo)