22. november 2012 - 09:55Der er
27 kommentarer og 1 løsning
Hvordan laves en central klasse som alle andre kan tilgå?
Hej,
Jeg har i mit program lavet en klasse til at logge data til en tekstfil. Denne klasse indeholder en public metode, som tager en string. Når metoden kaldes, åbner metoden en tekstfil, tilføjer den overgivne string som en ny linie, og lukker derefter filen. I grunden meget simpelt.
Denne metode i min log klasse vil jeg så kalde forskellige steder i mit program, f.eks. når programmet åbnes, programmet lukkes, eller når brugeren trykker på en bestemt knap osv.
Hvis jeg nu kun skulle bruge denne log-funktionalitet i min hovedform, ville jeg jo bare lave en instans a log-klassen (f.eks. kaldet log1), og så kalde log1.LogText("Program åbnet");
Opgaven er, at jeg skal logge alle mulige steder fra i mit program, og jeg synes det hurtigt bliver noget klyt, hvis jeg skal overgive en reference til hovedformen til alle klasser, som skal gøre brug af log-funktionen.
Man kan sikkert lave log-klassen global på en eller anden måde, med det skal laves korrekt i forhold til OOP.
Det er ikke en skoleopgave, det er meget længe siden, jeg har gået i skole :)
Jeg vil selv lave loggeren, da jeg så ved præcist, hvordan den virker, og den derved heller ikke bliver tungere end nødvendigt.
Da min log-klasse i dag er en UserControl, som bl.a. viser de loggede data i en ListView, kan jeg ikke se, at jeg kan bruge denne løsning, da der vel oprettes en instans i min hovedklasse lige så snart log-klassen (en UserControl fra Toolbox'en) smækkes på min hovedform??
Så synes jeg bare det smarte og overskuelige går af det, da singleton klassen ikke kommer til at indeholde andet kode end et par linier til at videresende en enkelt string. Det med at skrive til filen osv. kan jo puttes i UserControl'en, når nu jeg skal have den alligevel.
Jeg tygger lige på den lidt endnu, måske har andre også et andet forslag, som vi ikke har tænkt over...
Se nu skal du bare se, hvad jeg har fået min aften til at gå med :) Du spurgte til kode, så nu får du den.
1: Jeg har lavet en singleton klasse, som ikke laver andet end at tage imod en string, som den så sender af sted med et event, som den fyrer af. Her er koden til denne klasse: namespace xxx { public class ProgramLogSingletonClass { //Local instance of the class that is returned by GetInstance() method below private static ProgramLogSingletonClass _instance = null;
//Stuff for LogAdded event. ProgramLogEventArgs is a class that holds the values to be passed public delegate void LogEntryAddedHandler(object sender, ProgramLogEventArgs args); public event LogEntryAddedHandler LogEntryAdded;
//Constructor is private as this is a singleton class private ProgramLogSingletonClass() { //Do nothing, this is a singleton class }
//Public static methode to get instance of this class. //As this is a singleton class only one instance can exist, and this is created in this method public static ProgramLogSingletonClass GetInstance() { if(_instance == null) //First time this class is called { _instance = new ProgramLogSingletonClass(); }
return _instance; }
//This method is called whenever someone wants to add a new log item. //text is then passed with the event that the program logger user control is subscribed to public void AddLogEntry(string text) { if (LogEntryAdded != null) //Someone subscribed to the event { LogEntryAdded(this, new ProgramLogEventArgs(text)); } } } }
2: De værdier (i øjeblikket kun en enkel string), som bliver passet med event'et er indeholdt i en separat defineret klasse som hedder ProgramLogEventArgs. Koden for denne klasse kommer her: namespace xxx { //This class holds values that are passed with the event that is fired when a new program log has been added public class ProgramLogEventArgs: EventArgs { public string LogDescription { get; internal set; }
public ProgramLogEventArgs(string logDescription) { LogDescription = logDescription; } } }
3: Selve logningen med dertilhørende skrivning til fil, visning i ListView osv. sker i en UserControl-klasse, som subscriber til det event, som fyres af fra ProgramLogSingletonClass. Igen lidt relevant kode fra denne klasse: namespace xxx { public partial class ProgramLogUserControl : UserControl { ... public ProgramLogUserControl() { InitializeComponent();
//Subscribe to event that is fired when a new log entry has been added ProgramLogSingletonClass programLog = ProgramLogSingletonClass.GetInstance(); programLog.LogEntryAdded += ProgramLog_LogAdded; }
//Method that is called when subsribed event is fired void ProgramLog_LogAdded(object sender, ProgramLogEventArgs args) { AddLogEntry(args.LogDescription); }
AddLogEntry er en metode et andet sted i klassen, som skriver til fil, tilføjer til ListView osv.
Ser det helt tosset ud, det jeg har brygget sammen? Er det ikke et virvar af klasser for så simpelt en funktion, eller er det normalt i denne OOP verden? Hvis mine kommentarer ind imellem ikke er korrekte, må du meget gerne korrigere, da jeg i så fald ikke har forstået det kommenterede rigtigt.
De steder i programmet, hvorfra der skrives til loggeren, skal jo heller ikke subscribe til nogen events. De steder kalder bare: ProgramLogSingletonClass programLog = ProgramLogSingletonClass.GetInstance(); programLog.AddLogEntry("Test");
Det eneste sted der skal subscribes er fra loggerens ene halvdel (den del, hvor logdata vises visuelt) til den anden halvdel (singleton klassen). Dermed er det kun internt i loggeren, at denne teknik er anvendt. Udadtil er event'et ikke i spil.
Du kan også gøre som Arne_v siger, at man laver ILog som har ILogger.
Dvs:
public class Log { public static void AddLog(string msg); }
public interface ILogger { void Log(string msg); }
Hvor ILogger så kan logge til forskellige steder ... GUI, File, SQL.
Loggers er vel ofte et one time setup for en apps levetid. Fra start til slut. Nok måske derfor Arne_v siger, hvilket jeg nu godt kan se at Events måske er en mindre optimal måde at gøre det på.
Jeg tror jeg bliver ved den løsning, jeg nu har lavet, med mindre en af jer brokker jer højlydt. Iøvrigt har jeg herved lært lidt mere om singleton, events og delegates.
Iøvrigt må denne kunne laves som een linie, dog kan jeg ikke gennemskue hvordan: ProgramLogSingletonClass programLog = ProgramLogSingletonClass.GetInstance(); programLog.AddLogEntry("Test");
Udover det så synes jeg det det ser pångere ud at "ProgramLogSingletonClass" har en Instance property, som kun har en "get".
Jeg synes også du burde kalde den: "ProgramLog", så du ender med at gøre noget ala:
ProgramLog.Instance.AddLogEntry("test");
Udover det kan du lave en static metode på din "ProgramLogSingletonClass" som hedder "AddLogEntry" og så intern kalder "ProgramLogSingletonClass.GetInstance().AddLogEntry("test");"
Så vil du kunne logge, hvis du ændre tingene som beskrevet: ProgramLog.AddEntry("test")
Udover det så kan du også lave en set til din Instance, så du reelt set kan skifte din logger instance ud ... som en Bootstrap kald til din app. Det er vist også noget ala det log4net og nlog gør.
Håber det giver mening ... er lidt træt i dag kan jeg mærke :-)
lock er kun noedvendigt ved multithreaded, men det er ikke noget stort arbejde at saette den lock ind nu og husker man det den dag koden skal aendres fra singlethreaded til multithreaded
specielt ved server apps er der ofte et oenske om at driften kan konfigurere logningen saa logs ender hvor de vil have det (fil, database, eventlog/syslog etc.)
og hvis du udvider til at kunne logge mere eller mindre bliver konfiguration endnu mere interessant
jeg springer over point her - buzzzz har lavet alt arbejdet
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.