- Unir cadenas con comas
-
Hace unos dias Eric publico un problema, al principio me dio flojera responderlo, pero al ver el numero de respuestas decidi hacerlo agregando el reto personal de ser la respuesta mas corta (con mi equipo anterior competiamos en ver quien podia refactorizar mas), asi que aqui esta:
El problema:
Escribir una funcion que tome un IEnumerable no nulo y regrese una cadena con las siguentes caracteristicas: (1) Si la secuencia esta vacia, regresar "{}".
(2) Si la secuencia contiene un solo elemento "ABC" el resultado sera "{ABC}".
(3) Si la secuencia contiene 2 elementos "ABC", "DEF" El resultado sera "{ABC and DEF}".
(4) Si la secuencia contiene mas de 2 elementos "ABC", "DEF", "G", "H" el resultado sera "{ABC, DEF, G and H}". (Noten que los ultimos 2 elementos no estan unidos por coma)
Mi solucion:
static string JoinStrings(IEnumerable<string> strings) {
int len = strings.Count();
return len == 0 ? "{}"
: len == 1 ? "{"+strings.First()+"}"
: "{"+strings.Take(len - 1).Aggregate((string head, string tail) => head+", "+tail)+" and " +strings.Last()+"}";
}
Por cierto, si solo quisieramos unir las cadenas con comas (y regresar una cadena vacia cuando no hay elementos), pudieramos utilizar esto:
static string JoinStrings2(IEnumerable<string> strings) {
return strings.Count() > 0 ?
strings.Aggregate((string h, string t) => h+", "+t)
:
"";
}
- Los miembros de una interface SI pueden ser privados
-
Este dato sorprende a la mayoria, cualquiera que ha aprendido a usar interfaces sabe que la primera regla es que todos los miembros son publicos y por ende su implementacion tambien lo es, siempre, no es opcional. Al menos hasta que llegaste a este articulo a desaprender que eso no es totalmente cierto. Veamos un pequeño ejemplo, con el puro proposito de demostrarlo.
public interface ITest {
void Test();
}
public class Test : ITest {
void ITest.Test() { //<
Console.WriteLine("test");
}
public void Test2() {
Console.WriteLine("test2");
}
}
class Program {
static void Main(string[] args) {
ITest t = new Test();
t.Test();
Test t2 = new Test();
//t2.Test(); //<<=== no compila
Si no lo crees puedes intentarlo tu mismo.
Pero de que me sirve o para que puedo usar esto?
La tecnica te permite asegurarte que el metodo solo es visible a aquellos que usen una variable del tipo de la interface (ITest en este caso). Lo unico que hace es forzar a que el uso de ese metodo sea a travez de una instancia de la interface, osea que, para este ejemplo, si quieres acceder al metodo .Test, solo lo puedes hacer a travez de una variable del tipo ITest.
El unico "truco" para lograr esto es que en la declaracion tienes que agregar el tipo de la interface, antes del miembro:
void ITest.Test();
Seguramente no es el truco mas util, pero es algo para tenerlo guardado ahi en el cofre de los trucos y que en algun momento te sera de utilidad, o talvez algo que te pueda hacer ganar una apuesta ;)
- Obtener el ultimo dia del mes
-
Aplica a: C#, .NET
Entrando en la onda de compartir codigo, y creo que es la segunda vez en esta semana que necesito esta funcion
DateTime GetLastDayOf(DateTime date) {
return new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month));
}
- C# trivia #1: overloads, strings, nullable types
-
esta entra sirve a la vez para probar el mas reciente Windows Live Writer
Tenemos estas clase:
public class Foo {
public void Bar(string x) {
Console.WriteLine("string x was called");
}
public void Bar(int? x) {
Console.WriteLine("int? x was called");
}
}
cual sera la salida a la consola con el siguiente codigo:
Foo f = new Foo();
try {
f.Bar(null);
}
catch {
Console.WriteLine("no method was called");
}
- protected, internal, protected internal... de verdad sabes que hacen?
-
Este articulo aplica a C#
Todos creemos dominar los modificadores de acceso, pero me puedes decir lo que protected internal hace?
apuesto que mas de la mitad de los desarrolladores .NET contestarian incorrectamente, vamos a ver:
- private:
- Este es el nivel de acceso mas restrictivo.
- Es accesible solamente dentro del cuerpo de la clase o estructura en que esta declarado.
- Tipos declarados dentro (nested types) del mismo cuerpo de la clase tambien tienen acceso a los tipos privados.
- public:
- Este es el nivel de acceso con menos restricciones.
- No hay restricciones para accesar miembros publicos, son tan visibles como la clase.
- protected:
- El acceso es limitado a la clase que los contiene o a tipos derivados de la clase.
- Lo cual quiere decir que no puedes crear una instancia de esta clase y accesar los miembros protegidos.
- internal:
- El acceso esta limitado solo al assembly en donde la clase reside.
- protected internal:
- Acceso es limitado a el assembly donde la clase reside o a tipos derivados de la clase.
Este ultimo es bastante capcioso, a primera vista podria parecer que permite acceso solo dentro del assembly donde la clase reside y las clases derivadas, pero el "o" ahi es lo que hace la diferencia, si marcas una propiedad protected internal puede ser usada fuera del assembly actual. basicamente protected sobreescribe internal, asi que ni siquiera estoy seguro de cuando es que se podria usar tal modificador.
pero bueno, regresando al tema, si no me crees eso de protected internal, puedes verificar tu mismo:
Assembly1:
public class Test {
protected internal string prop1;
}
Assembly2://despues de agregar la referencia a Assembly 1
public class Test2: Test {
public string prop2;
public Test2() {
prop2 = prop1; //el acceso a prop1 viene de otro assembly, de un miembro protected internal
}
}
Si esto es nuevo para ti, podrias pensar que es absurdo, entonces como haces que una propiedad sea visible solo en el assembly actual y en las clases derivadas (pero solo de este assembly)?
Pues no es tan complicado, marcas la clase como internal, lo cual restringe el acceso solo al assembly actual, y luego marcas la propiedad como protected, lo cual resulta en acceso desde las clases derivadas pero solo dentro del assembly actual.:
internal class Test {
//accesible solo en este assembly y clases derivadas dentro de este assembly
protected string prop1;
} salu2
- formateando 0, 1 como si, no, etc
-
Esto es algo que todos (los programadores) hemos hecho alguna vez, convertir un valor 0, 1 al equivalente si o no, prendido o apagado, verdadero o falso, valor en un string
pues resulta que hay una funcion en el BCL con la cual podemos hacer exactamente eso, string.Format es dicha funcion
Console.WriteLine(string.Format("{0:si;;no}", 0)); //salida "no"
Console.WriteLine(string.Format("{0:si;;no}", 1)); //salida "si"
y nomas por ver que pasaba
Console.WriteLine(string.Format("{0:si;;no}", -1)); //salida "-si"
...Esas pequeñas cosas
- funcion para generar numeros aleatorios, con todos los digitos distintos
-
El codigo para este articulo aplica a C#
Leyendo blogs, me encontre con este post (en italiano), donde Marco quiere escribir una funcion que le genere numeros aleatorios de 5 digitos, el resultado se requiere en un string, donde todos los digitos son diferentes, es decir, cada numero (de 5 digitos) generado, no puede tener 2 digitos iguales, la funcion que el escribio es la siguiente:
private string GetRandom()
{
Random rn = new Random();
string resultnum=string.Empty;
do
{
string a = rn.Next(0, 9).ToString();
if (resultnum.Contains(a)!=true)
resultnum = resultnum + a;
}
while (resultnum.Lengthreturn resultnum;
}
Inmediatamente me dio curiosidad por ver que areas que podria mejorar, y me puse a escribir una funcion que obtuviera el mismo resultado, pero en forma mas optimizada
cosas que saltan a mi mente inmediatamente son:
- concatenacion de strings
- el loop y la comparacion para encontrar numeros que no tengamos previamente
Entonces lo que quise lograr en mi funcion es: evitar la concatenacion, y ejecutar el loop exactamente 5 veces, esto es el resultado:
static char[] allNumbers = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
Random r = new Random();
public string GetRandom2() {
char[] result = new char[5];
int max = 9, pos=0;
for (int i=1; i//*** swap positions
allNumbers[pos] ^= allNumbers[max];
allNumbers[max] ^= allNumbers[pos];
allNumbers[pos] ^= allNumbers[max--];
}
return new string(result);
} La tecnica que use,
- fue tener un arreglo predefinido de caracteres con todos los digitos,
- luego tengo una variable max que uso para llamar el metodo Random.Next(0, max),
- en cada iteracion del loop decremento esta variable,
- y cambio el numero que resulto elegido a la ultima posicion, con esto lo dejo fuera de las posibilidades para la siguiente llamada
Estos cambios me dieron una ganancia realmente minima (3 milesimas por cada 1000 llamadas), luego se me ocurrio mover la declaracion de la variable Random afuera de la funcion, para que se reusara con cada llamada a este metodo, y eso si que me dio una ganancia tremenda, llamando la funcion 10,000 veces, el metodo original me da 114ms, y el metodo nuevo 6ms, asi que ahi era donde estaba realmente el problema, lo demas es casi insignificante =o(
Seguro alguien mas puede crear una funcion mas rapida, pero yo cumpli mi objetivo =o)
salu2