====== C# Xtra (II) ======
===== Sockets =====
* [[code:tools#sockets|Explicación de sockets]].
==== Creación de un servidor RAW ====
System.Net.Sockets.TcpListener server = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Any, 2005);
server.Start();
Byte[] bytes = new Byte[256];
String data = null;
while (true)
{
System.Net.Sockets.TcpClient client = server.AcceptTcpClient();
data = null;
System.Net.Sockets.NetworkStream stream = client.GetStream();
int i;
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);
data = data.ToUpper();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
stream.Write(msg, 0, msg.Length);
Console.WriteLine("Sent: {0}", data);
}
}
Para enviar un "salto de línia" haremos:
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data + "\n\r");
stream.Write(msg, 0, msg.Length);
===== Expresiones Regulares =====
==== In a nutshell ====
En ''System.Text.RegularExpressions'' existe la clase ''Regex'' que se encarga de localizar combinaciones (matches) de expresiones regulares en un texto. Por ejemplo, su método estático ''Matches'' recibe un texto y una expresión regular y devuelve objetos ''Match'' de las coincidencias. \\
Un objeto ''Match'' tiene las siguientes propiedades interesantes:
* ''NextMatch()'':
* ''Index'': Posición dentro del string original donde se encuentra el primer carácter de la coincidencial.
* ''Lenght'':
* ''Success'':
* ''Value'': Recoge el substring de la coincidencia encontrada.
string startBy = "src=\"|src = \"|src= \"|src =\"";
var matches = System.Text.RegularExpressions.Regex.Matches(text, startBy, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
List lImgNames = new List();
for (int i = 0; i < matches.Count; i++)
{
var match = matches[i];
int start = match.Index + match.Value.Length;
int end = text.IndexOf("\"", start);
string file = text.Substring(start, end - start);
lImgNames.Add(file);
}
===== Comunicación entre procesos =====
==== Canales IPC ====
Son utilizados para realizar una comunicación entre procesos que están corriendo en la misma máquina (IPC, //inter-process communication//). Y son más rápidos que utilizar rutinas sobre red, son algo parecidas a las //Named Pipes// existentes en el //kernel32.dll//. \\
Para poder usar dichos canales se necesitan los siguientes elementos:
- Objeto cliente.
- Objeto servidor.
- Objetos compartidos, referenciados tanto por el cliente como por el server pero que a la vez no referencien a ninguno de ellos.
También necesitaremos agregar una referencia a //System.Runtime.Remoting//. Y hacer ''using'' de unos cuantos namespaces:
using System.Runtime.Remoting.Channels.Ipc;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;
=== El dominio de la aplicación ===
Un dominio de aplicación es un proceso dentro del sistema operativo donde una o más aplicaciones residen. Los objetos que se encuentran en el mismo dominio de aplicación se comunican directamente, en cambio, los que están en distintos dominios se comunican a partir del transporte de copias de estos objetos. Existe una clase (**System.MarshalByRefObject**) que sirve como clase base para las clases que intervengan la comunicación e intercambio de mensajes entre dominios de aplicación (si no heredasen serían //marshal by value//).
=== Los objetos compartidos ===
Necesitaremos crear la clase de los objetos que se compartirán (ha de heredar de ''MarshalByRefObject'').
namespace SharedObjects {
public class Class1 : MarshalByRefObject {
string v;
public string Value {
get {
return this.v;
}
set {
this.v = value;
}
}
}
}
=== El servidor ===
Creamos un canal de servidor a partir de un nombre de canal (en formato string) y lo registramos mediante el método estático de la clase ''ChannelServices'', a este se le pasan dos objetos, un objeto correspondiente al canal en sí y un boolean, que será //true// si el canal será manejado a partir de un protocolo seguro. \\ \\
Luego deberemos registrar los objetos compartidos a partir del método estático ''RegisterWellKnownServiceType'' dentro de ''RemotingConfiguration'', si lo hacemos mediante ''WellKnownObjectMode.Singleton'' se creará un único objeto objeto, para acceder a él la ruta será ''ipc://nombreDelCanal/NombreDelObjeto''. \\ \\
Para recoger un objeto utilizaremos el método ''GetObject'' de la clase ''Activator''.
IpcServerChannel server = new IpcServerChannel("myChannel");
ChannelServices.RegisterChannel(server, false);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(SharedObjects.Class1), "sharedObjs", WellKnownObjectMode.Singleton);
SharedObjects.Class1 c = (SharedObjects.Class1)Activator.GetObject(typeof(SharedObjects.Class1), "ipc://myChannel/sharedObjs");
c.Value = "Servidor";
=== El cliente ===
Únicamente es necesario acceder al objeto a partir de ''Activator.GetObject''.
// IpcClientChannel icc = new IpcClientChannel();
// ChannelServices.RegisterChannel(icc, true);
// RemotingConfiguration.RegisterWellKnownClientType(typeof(SharedObjects.Class1), "ipc://myChannel/sharedObjs");
SharedObjects.Class1 c = (SharedObjects.Class1)Activator.GetObject(typeof(SharedObjects.Class1), "ipc://myChannel/sharedObjs");
Console.WriteLine(c.Value);
===== Configuración =====
==== Fichero app.config ====
El fichero que configura la aplicación es el ''app.config'' y se ha de encontrar en el directorio donde está el ensamblado ejecutable de la aplicación.
=== Leer un valor ===
Para leer el valor //logFile// de un fichero de configuración como el que sigue:
Utilizaremos un objeto de la clase **AppSettingsReader** y su método ''GetValue'':
(string)new System.Configuration.AppSettingsReader().GetValue("dbFile", typeof(String));
=== Agregar secciones ===
Podemos agregar secciones personalizadas al fichero, por ejemplo una sola para el tema de ''log'', para ello deberemos declararla entro de la tag ''configSections'' indicando nombre y tipo como sigue:
Para leer estas secciones deberemos agregar la referencia al ensamblado ''System.Configuration'' y poder así utilizar la clase ''ConfigurationManager'':
IDictionary confTable = (IDictionary)System.Configuration.ConfigurationManager.GetSection("log");
string logFile = (string)confTable["logFile"];
=== Agregar grupos de secciones ===
Podemos agregar grupos de secciones, por ejemplo:
...
Y luego acceder a ellas como si accediesemos a una sección personalizada:
IDictionary confTable = (IDictionary)System.Configuration.ConfigurationManager.GetSection("atm.ws/dbInfo");
string dataSource = (string)confTable["DataSource"];
===== Programación declarativa con C# =====
* [[code:best-practices#programacion_declarativa|Programación declarativa]]
==== Métodos ====
Podemos realizar un bucle ''foreach'' con el método ''List.ForEac'' y una expresión lambda.
using System;
using System.Collections.Generic;
class Example
{
static void Main()
{
new List { 1, 2, 3 }
.ForEach(i => Console.WriteLine(i));
}
}
De una forma parecida podemos sacar partido del delagado ''Action'', el cual permite una funcion ''Action'' el cual encaja con ''Console.WriteLine''.
using System;
using System.Collections.Generic;
class Example
{
static void Main()
{
new List { 1, 2, 3 }
.ForEach(Console.WriteLine);
}
}
También el método ''Enumerable.Range'':
using System;
using System.Linq;
class Example
{
static void Main()
{
Int32 sum = Enumerable.Range(0, 99)
.Where(i => i % 2 == 0)
.Sum();
Console.WriteLine(sum);
}
}