Avatar billede HPCISCO Nybegynder
22. november 2012 - 09:55 Der 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.

Nogen gode forslag?

Tak på forhånd!
Avatar billede Syska Mester
22. november 2012 - 10:03 #1
public static metode på din Logger class.

Den kan så indeholde en måde at finde en instance af den klasse som er singleton for hele din app.

Husk at gøre det multi thread safe.

mvh
Avatar billede HPCISCO Nybegynder
22. november 2012 - 10:10 #2
Da jeg er forholdsvis ny indenfor C# bliver du nok nød til at forklare lidt mere...

I dag ser min klasse sådan ud:
namespace xxx
{
    public partial class SystemLog : UserControl
    {
...
    public void AddLogEntry(string text)
        {
...
}
Avatar billede Syska Mester
22. november 2012 - 10:16 #3
Avatar billede HPCISCO Nybegynder
22. november 2012 - 11:36 #4
Er nu i fuld gang med at læse om singleton klasser.

Det spiller vel ikke sammen med, at min klasse i dag er en UserControl?
Avatar billede Syska Mester
22. november 2012 - 11:50 #5
Jo, det har ingen betydning.

Singleton kan du altid tilgå ...

Hvis det er en skole opgave bør du selv lave det, men hvis det er til et projekt kan du tage et kig på http://logging.apache.org/log4net/ eller http://nlog-project.org/

mvh
Avatar billede Syska Mester
22. november 2012 - 11:51 #6
projekt som i "det bare skal virke og gennemtestet" så ville jeg nok gå efter en af 2 overstående.

Projekt i forbindelse med opgave fra en udd. bør du selv lave det.
Avatar billede HPCISCO Nybegynder
22. november 2012 - 12:06 #7
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??

Eller er jeg helt galt på den?
Avatar billede Syska Mester
22. november 2012 - 12:30 #8
Hvorfor ik?

Det er jo den samme der så bliver brugt eller steder, så hvorfor mener du det ikke ville virke?
Avatar billede Syska Mester
22. november 2012 - 12:31 #9
Tro mig ... log4net og nlog er optimeret :-) De gør skam ikke noget som ikke er nødvendigt.

Vi bruger log4net hvor jeg arbejder, og det spiller bare.
Avatar billede HPCISCO Nybegynder
22. november 2012 - 14:00 #10
Jeg ved ikke, hvordan jeg skal forklare det, jeg ikke forstår, men se her:

1:
Jeg har ændret min klasse (UserControl) til at være singleton (med private constructor osv.).

2:
Jeg trækker min UserControl fra Toolbox'en over på min hovedform, hvorved kun følgende kode autogenereres i "code-behind filen".

//
            // systemLog1
            //
            this.systemLog1.Location = new System.Drawing.Point(385, 51);
            this.systemLog1.Name = "systemLog1";
            this.systemLog1.Size = new System.Drawing.Size(222, 221);
            this.systemLog1.TabIndex = 0; 

Her mangler jo en instantiering, og det har selvfølgelig en runtime fejl til følge, når programmet udføres.
Avatar billede Syska Mester
22. november 2012 - 14:19 #11
Du UserControl som viser log skal ikke være singleton, men din klasse som logger skal være singleton.

Du har ikke vist meget kode, så jeg antager måske for mange ting.

Men kort sagt ... så vil du logge fra X antal steder og hvergang skal du vise det i din usercontrol, right?

Så kunne du smide et event på din logger class som din user control kan subscribe til ... og på den måde få de logs som måtte komme.

Når så du laver:
SomeLoggerClass.WriteLog("Some event") <- Så skal denne metode raise et event som din usercontrol så er subscribed til.
Avatar billede HPCISCO Nybegynder
22. november 2012 - 14:38 #12
Korrekt.

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...
Avatar billede HPCISCO Nybegynder
22. november 2012 - 23:31 #13
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.
Avatar billede arne_v Ekspert
23. november 2012 - 01:36 #14
du mangler en lock i den singleton
Avatar billede arne_v Ekspert
23. november 2012 - 01:38 #15
traditionelt foretraekker man at konfigurere loggere fremfor at lade applikationen subscribe til events
Avatar billede HPCISCO Nybegynder
23. november 2012 - 08:21 #16
1:
Mht. lock er det vel kun nødvendigt, hvis mere end en thread skal tilgå den?

2:
Hvad mener du med at konfigurere loggeren?
Avatar billede HPCISCO Nybegynder
23. november 2012 - 10:30 #17
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.

Har jeg ret i dette synspunkt?
Avatar billede Syska Mester
23. november 2012 - 11:59 #18
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å.

mvh
Avatar billede HPCISCO Nybegynder
23. november 2012 - 12:26 #19
Respekt at du kan udlede så meget ud fra arne_v's to linier :)

Før jeg kan forstå det, skal der vist lidt mere forklaring til.
Hvad er placeret i singleton klassen?
Avatar billede Syska Mester
23. november 2012 - 12:36 #20
Kig på log4net og nlog. De er implementeret som Arne_v skriver. De er begge open source, så det er bare at grave rundt.
Avatar billede HPCISCO Nybegynder
23. november 2012 - 12:46 #21
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.

Læg svar som tak!
Avatar billede Syska Mester
23. november 2012 - 12:58 #22
Svar.

Det hele går jo ud på at blive klogere :-) og de tkan jeg høre er lykkedes. Så dermed er min mission også fuldført.

Synes også Arne_v bør have lidt point. Han er min .NET master guru :-). Der kan man altid lære noget.
Avatar billede HPCISCO Nybegynder
23. november 2012 - 13:07 #23
Det var vidst ikke et svar!
arne, hvad med dig?

Iøvrigt må denne kunne laves som een linie, dog kan jeg ikke gennemskue hvordan:
ProgramLogSingletonClass programLog = ProgramLogSingletonClass.GetInstance();
            programLog.AddLogEntry("Test");

Kan du/I?
Avatar billede Syska Mester
23. november 2012 - 13:16 #24
ProgramLogSingletonClass.GetInstance().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 :-)

Det er jo også fredag :-p

Her kommer et svar :-)
Avatar billede arne_v Ekspert
24. november 2012 - 01:58 #25
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
Avatar billede arne_v Ekspert
24. november 2012 - 01:58 #26
fordelene ved konfig fil fremfor kode er at det kan aendres efter behov uden at der skal rettes i koden
Avatar billede arne_v Ekspert
24. november 2012 - 02:02 #27
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
Avatar billede arne_v Ekspert
24. november 2012 - 02:03 #28
jeg springer over point her - buzzzz har lavet alt arbejdet
Avatar billede Ny bruger Nybegynder

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.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester