| Johan's profileJohan Hernandez - Cross ...BlogNetwork | Help |
Johan Hernandez - Cross Platform Developer.NET, Mono, Windows, Linux, Technology, C#, D Programming Language, Web Development |
|||||
|
|
November 24 Polymorphic Cloneable Class ImplementationYou may encounter some issues while implementing the .NET Cloneable Objects with Polymorphism, this is because the way ICloneable interface was declared is the way is supposed to work interfaces in OOP: describing "what it does"(declaration) instead "how it does"(definition/implementation). I'm not criticizing the ICloneable interface, it's fine for me and i clearly don't see other way to implement it. This post is a recommendation of how make a Polymorphic Cloneable Class Implementation. Consider the following classes: 1: abstract class Thing 2: {3: public string FullName { get; set; } 4: } 5: 6: class Human : Thing 7: {8: public string FirstName { get; set; } 9: public string LastName { get; set; } 10: } 11: 12: class Computer : Thing 13: {14: public string Manufacturer { get; set; } 15: public string Model { get; set; } 16: }what's the point? I want all my 'Things' to be cloneable, so it's obvious that the ICloneable implementation must be in Thing but then all the derived class can't participate in the cloning phase. also, how can Thing make an instance of itself being abstract? CreateClone: let a derived class create the instance of Thing. PrepareClone: let the Thing class clone their own state and also let the derived classes to clone their own state too in the same instance. This method is invoked next to the CreateClone method. 1: abstract class Thing : ICloneable 2: 3: public string FullName { get; set; } 4: 5: #region ICloneable Members 6: public object Clone() 7: { 8: var clone = CreateClone(); 9: PrepareClone(clone);10: return clone; 11: }12: #endregion 13: protected abstract Thing CreateClone(); 14: protected virtual void PrepareClone(Thing thisThing) 15: {16: thisThing.FullName = this.FullName; 17: } 18: 19: 20: lass Human : Thing 21: 22: public string FirstName { get; set; } 23: public string LastName { get; set; } 24: protected override Thing CreateClone() 25: {26: return new Human(); 27: }28: protected override void PrepareClone(Thing thisThing) 29: {30: base.PrepareClone(thisThing); 31: var human = thisThing as Human; //this is always Human or a Derived of. 32: human.FirstName = this.FirstName; 33: human.LastName = this.LastName; 34: } 35: 36: 37: lass Computer : Thing 38: 39: public string Manufacturer { get; set; } 40: public string Model { get; set; } 41: protected override Thing CreateClone() 42: {43: return new Computer(); 44: }45: protected override void PrepareClone(Thing thisThing) 46: {47: base.PrepareClone(thisThing); 48: var human = thisThing as Computer; //this is always Computer or a derived of. 49: human.Manufacturer = this.Manufacturer; 50: human.Model = this.Model; 51: }Usage: 1: static void Main(string[] args) 2: {3: ICloneable pc = new Computer() 4: {5: Manufacturer = "Dell", 6: Model = "XPS", 7: FullName = "Dell XPS" 8: }; 9: Computer clonedPc = (Computer)pc.Clone(); 10: }November 07 Indexers in D Programming Language(opIndex and opIndexAssign usage)opIndex is a D programming language special function which is invoked when an index is requested from a instance of a class. you can implemente indexes in your class too. 1: import tango.io.Stdout; 2: 3: class Person 4: {5: public int age; 6: public char[] name; 7: int opIndex(char[] name) 8: {9: if(name =="Age") 10: return this.age; 11: else throw new Exception("Invalid property"); 12: } 13: } 14: 15: void main() 16: {17: scope person =new Person; 18: person.age = 56;19: int age = person["Age"]; 20: Stdout(age); 21: }The above example program returns 56 that is the result of the opIndex call in the class. opIndex is the "getter" of the index, if you want your index to be writtable you will need implement opIndexAssign which becomes to be the "setter" of you indexer as the following code: 1: import tango.io.Stdout; 2: 3: class Person 4: {5: public int age; 6: public char[] name; 7: int opIndex(char[] name) 8: {9: if(name =="Age") 10: return this.age; 11: else throw new Exception("Invalid property"); 12: }13: void opIndexAssign(int value,char[] name) 14: {15: if(name =="Age") 16: this.age = value; 17: else throw new Exception("Invalid property"); 18: } 19: } 20: 21: void main() 22: {23: scope person =new Person; 24: person["Age"] = 56; 25: int age = person["Age"]; 26: Stdout(age); 27: }The above code also print 56 in the console just like the first example, but in this case we are asigning values using the Indexer instead putting the value directly into the public variable. it's all about overload.Both opIndex and opIndexAssign can be overloaded to allow you create indexers of any type. what am i talking about? well, the above examples works with char[] as input and int as return type but you can change the data types of the example to fit your needs. The following example signatures will help you to understand this:
easy, right? right? :) Enjoy D. October 28 Basic Network Addresses Detection in C#I have created a basic application for detecting network IP addresses as an introduction to System.Net namespace. Basically we use the method Dns.GetHostAddresses(string) and show the result in a form, this is an screenshot of the app. (the UI is on spanish for the screenshot because my Regional Setting) you can download the source code right here. August 09 DoEvents en WPFAl grano con la solucion. 1: using System; 2: using System.Windows.Threading; 3: 4: namespace Johansoft 5: {6: public static class WpfUtil 7: {8: public static void DoEvents() 9: {10: DispatcherFrame frame = new DispatcherFrame(); 11: Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,12: new DispatcherOperationCallback((f) => 13: {14: ((DispatcherFrame)f).Continue = false; return null; 15: }), frame); 16: Dispatcher.PushFrame(frame); 17: } 18: } 19: }Explicacion: No existe un metodo en WPF que realice la funcion de Application.DoEvents, sin embargo podemos crearlo nosotros mismos. DoEvents espera a que todas las operaciones de interfaz grafica(encoladas o 'queued') se realicen antes de continuar exactamente despues de la llamada al metodo. En WPF tenemos mas control de esta cola y las operaciones mediante los Dispatchers y los Frames. Lo que hacemos basicamente en WPF es un DispatcherFrame y asincronicamente denegamos la continuacion de la ejecucion hasta que todas las operaciones han sido completadas. July 07 Ejecutar y Capturar Salida Continua de Proceso con C#En el namespace System.Diagnostics tenemos las clases Process and ProcessStartInfo. El metodo Process.Start nos permite ejecutar un proceso usando una instancia de ProcessStartInfo como se muestra a continuacion: 1: ProcessStartInfo si = new ProcessStartInfo(@"zip.exe"); 2: Process p = Process.Start(si);El codigo anterior ejecutara el archivo llamado zip.exe, sin embargo no captura la salida del proceso. Necesitamos realizarle unos ajustes a la instancia de ProcessStartInfo de esta manera:
1: ProcessStartInfo si = new ProcessStartInfo(@"zip.exe"); 2: si.RedirectStandardOutput = true; 3: si.UseShellExecute = false; 4: Process p = Process.Start(si);En la linea 2 y 3 ahora vemos los ajustes, con RedirectStandardOutput con valor true le decimos que necesitamos trabajar o leer el buffer de salida del proceso que estamos ejecutando(zip.exe) y UseShellExecute es requerido para poder establecer la primera propiedad en true ademas de evitar que windows cree una ventana de linea de comandos para mostrarnos la ejecucion del archivo, ahora se ejecutara en modo silencioso y solo puede detenerse usando el metodo Stop de la clase process o con el administrador de tareas de Windows. Aunque estamos redireccionando el buffer de salida aun no estamos trabajando con el, para poder leerlo usamos la propiedad StandardOutput que es de tipo System.IO.StreamReader. Recordemos que el proceso puede detenerse, demorarse o suspender la escritura en la salida asi que practicamente nuestro programa debe aguardar para leer mas datos de la salida del sub-programa. Para no detener nuestra aplicacion hasta que se haga una escritura en el sub-programa entonces usaremos Threads. Como esta es una tarea sencilla y no se necesitra control del Thread entonces ThreadPool, ThreadPool contiene un metodo llamado QueueUserWorkItem que recibe un metodo a ejecutar y lo asigna a un Thread que este disponible para ejecutar la operacion. Ejemplo: 1: ThreadPool.QueueUserWorkItem(delegate 2: {3: char c = char.MinValue; 4: while ((c = (char)p.StandardOutput.Read()) != 0) 5: {6: if (this.InvokeRequired) 7: this.Invoke(new EventHandler(delegate 8: {9: this.textBox1.AppendText(c.ToString()); 10: }));11: else 12: {13: this.textBox1.AppendText(c.ToString()); 14: } 15: } 16: });En el codigo anterior, la linea 4 inicia un bucle para leer 1 caracter de la salida del sub-programa a la vez mientras no sea equivalente a 0, el 0 significa que no hay mas nada que leer. Como el ejemplo anterior esta realizado sobre un Form, las lineas 6 y 14 actualizan un pequeño TextBox agregandole el nuevo caracter leido de la salida del sub-programa. Cuando se trabajan con Threads y interfaz grafica de Windows(Forms,controles,etc) los cambios en las propiedades de la interfaz grafica(controles, forms) realizados desde otro Thread pueden ser peligrosos y pueden traer resultados inesperados, por eso las lineas 9 y 13 hacen exactanmente lo mismo pero en un contexto diferente, tal vez en otro post explique mejor el asunto. Finalmente: 1: ProcessStartInfo si = new ProcessStartInfo(@"zip.exe"); 2: si.RedirectStandardOutput = true; 3: si.UseShellExecute = false; 4: Process p = Process.Start(si);5: ThreadPool.QueueUserWorkItem(delegate 6: {7: char c = char.MinValue; 8: while ((c = (char)p.StandardOutput.Read()) != 0) 9: {10: if (this.InvokeRequired) 11: this.Invoke(new EventHandler(delegate 12: {13: this.textBox1.AppendText(c.ToString()); 14: }));15: else 16: {17: this.textBox1.AppendText(c.ToString()); 18: } 19: } 20: }); |
||||
|
|