Avatar billede koppelgaard Praktikant
17. oktober 2009 - 09:03 Der er 23 kommentarer og
1 løsning

strømlining af kode

Jeg har et projekt, som godt kunne trænge til at blive ryddet op i.
Dette er beskrevet i http://www.eksperten.dk/spm/889431 og delvist besvaret af kza.'
Her er opklarinde spørgsmål til hans svar:


'Hvorfor blande DataGridView ind i at ZedGraphControl skal opdateres, når der tilføjes data til DataTable?', spørger du ?
Årsagen er ,at jeg  kunne finde på at slette enkelte rækker i datagridwievet og ved denne sletning godt vil holde zedgrafen opdateret. Der forekom mig at være en nem måde.
Men måske har jeg ikke ret?

Jeg har valgt at skrive catch-output til en fil., men hvor er det EventLog.WriteEntry kan ses?
Er det det der popper op ned e i debugvinduet

Hvordan får jeg Ksat til at lytte til en event i sartorius?

Kan du uddybe forbedringerne under form load?
Er ideen at kunne tilgå objekterne direkte fra formen uden at skulle gennem  KsatClass ? Ksat var jo tænkt, som en slags controller.

Michael
Avatar billede kza Nybegynder
17. oktober 2009 - 19:05 #1
Mine råd bygger på nogle generelle principper. Hvis du laver det program færdig nu og så aldrig skal kigge på det igen, så giver de ikke mening. Med mindre dit program alligevel ikke kan/skal bruges, så vil der dog sikkert dukke ønsker op om ændringer eller nye features, og der vil skulle rettes fejl. Så rådene går på, at når noget skal ændres, så skal man ind og rette så få steder som muligt. Og hvis nyt skal tilføjes, så er det let at 'plugge' ind i det eksisterende.

En god ide i denne sammenhæng er at forsøge at adskille din databehandling fra din GUI mest muligt. Hvis du sletter rækker i din grid, fjernes de også fra din DataTable, der fyrer RowChanged events. Du kan altså også i dette tilfælde bruge en eventhandler på DataTable.RowChanged til at opdatere zedgrafen.

catch: Det er System.Diagnostics.Debug.WriteLine, der skriver til Output.
EventLog.WriteEntry() skriver til eventloggen, som du finder i Kontrol Panel | Administration | Logbog. Du kan også bare skrive til en fejllog-fil. Det vigtige er, at når dit program er taget i brug, og det lige pludselig går i sort, så er der et sted, hvor du kan se noget om, hvad det var der skete.

Events:

Der er en standard opskrift for at tilføje events til en klasse, lad os kalde eventen DataAvaiable:

1. Da du ikke bare skal fyre en event, men også levere data med i den event, så skal du lave en EventArgs klasse, der kan indeholde dine data:

    public class DataAvaiableEventArgs : EventArgs
    {
        public string Data { get; set; }
    }

2. Dernæst skal du tilføje en event inde i Sartorius klassen:

    public event EventHandler<DataAvaiableEventArgs> DataAvailable;

3. Tilføj nu en hjælpe-metode til Sartorius klassen:

    void OnDataAvaiable(string data)
    {
        DataAvaiableEventArgs e = new DataAvaiableEventArgs() { Data = data };
        EventHandler<DataAvaiableEventArgs> d = DataAvailable;
        if (d != null)
            d(this, e);
    }

Hvis ingen lytter på eventen så er DataAvaiable lig null, så man skal lige tjekke den først. d er indført for at undgå at falde i en multitråds-fælde.

4. I steden for at kalde ksat.DataToGrid(dataOut) skal du kalde OnDataAvaiable(dataOut). Fjern KsaClass fra Sartorius.

Du har nu en DataAvaiable event på Sartorius klassen, og skal lytte på den:

I KsatClass constructor:
    serclass.DataAvailable += serclass_DataAvailable;

Tilføj
    void serclass_DataAvailable(object sender, DataAvaiableEventArgs e)
    {
        DataToGrid(e.Data);
    }


Form_Load: Du gør noget fem gange, så lav en funktion og kald den fem gange. Desuden kan det være en god ide, at udskille oprettelsen af objekter i en metode. Man kalder det en Factory metode - den 'fabrikerer' objekter. Noget a la:

KsatClass CreateKsatClass(int oNr, string portName, ZedGraphControl zgc, DataGridView dgv, Timer timer)
{
    Sartorius sar = new Sartorius(portName, 7, Parity.Odd, 1200, this);
    return new KsatClass(oNr, zgc, dgv, sar, timer, this);
}

Du kalder så CreateKsatClass fem gange i MainForm_Load.


Og så ville jeg nok bevæge mig i retning af:
KsatClass CreateKsatClass(string portName, ZedGraphControl zgc, DataGridView dgv, Timer timer)
{
    Sartorius sar = new Sartorius(portName, 7, Parity.Odd, 1200);
    ZedView zv = new ZedView(dgv, zgc);   
    return new KsatClass(zv, sar, timer);
}

KsatClass controlleren 'kontrollerer' ZedView, der indkapsler DataGridView og ZedGraphControl. Hvis du på et tidspunkt vil udskifte DataGridView med en mere advanceret 3. parts grid, så skal du selvfølgelig udskifte den på FormMain, men så er det ellers kun ZedView, du skal ind og ændre i.


/Klaus
Avatar billede koppelgaard Praktikant
17. oktober 2009 - 19:14 #2
1000 tak for dit svar!
Jeg ser på det i aften eller i morgen formiddag.

Michael
Avatar billede koppelgaard Praktikant
18. oktober 2009 - 21:28 #3
Nu fik jeg eventen til at virke men forstår ikke kodelinierne:

EventHandler<DataAvaiableEventArgs> d = DataAvailable;
        if (d != null)
            d(this, e);

Hvorfor er det forresten ikke godt, at have referencer begge veje?

Jeg skal helt klart have lave catch sætninger om.
Tror jeg laver en metode, som jeg kalder i tilfælde af fejl.
Avatar billede Syska Mester
18. oktober 2009 - 22:41 #4
Han ser bare om den er null, dvs om er nogen der lytter på eventen. Hvis den ikke er null, er den nogen der lytter og han sender data ud til dem som lytter.

Men nu kommer jeg selv lidt i tvivl. Hvis der er flere der lytter, er det så samme EventArgs object de alle får ?

til det sidste spm:
Hvis alle kender til alle, kunne du i princippet ligeså godt have alt i en klasse.

Og som han osgå skriver ... det gør det svært at skifte ting ud, uden at det har en effekt på hele programmet.

// ouT
Avatar billede koppelgaard Praktikant
19. oktober 2009 - 07:42 #5
okay tak:-)
Avatar billede kza Nybegynder
19. oktober 2009 - 08:42 #6
Ok. Hvis du skriver

    void OnDataAvaiable(string data)
    {
        DataAvaiableEventArgs e = new DataAvaiableEventArgs() { Data = data };
        DataAvaiable(this, e);
    }

og der ikke er nogen, der lytter på eventen. Dvs, der er ikke nogen, der har lavet en
    serclass.DataAvailable += serclass_DataAvailable;

så er DataAvaiable null, og du får en exception.

Så kunne vi jo skrive:
    void OnDataAvaiable(string data)
    {
        DataAvaiableEventArgs e = new DataAvaiableEventArgs() { Data = data };
        if (DataAvaiable != null)
            DataAvaiable(this, e);
    }

Og det vil altid gå godt i dit tilfælde. Indtil du på et eller andet tidspunkt finder på, at KsatClass under specielle omstændigheder skal holde op med at lytte på eventen med en
    serclass.DataAvailable -= serclass_DataAvailable;

Alt virker stadig, dit program kører i 2 måneder, og så kommer lige pludselig en NullReferenceException i linien
    DataAvaiable(this, e);

... men det virkede jo i går!

Det fejler kun, når timingen er uheldig. Scenariet er:

Din baggrundstråd har lige udført
        if (DataAvaiable != null)
hvor DataAvaiable ikke var Null.
Windows beslutter, at nu er det tid til et skift, stopper din baggrundstråd og lader din forgrundstråd køre.
Forgrundstråden udfører nu
    serclass.DataAvailable -= serclass_DataAvailable;

Hvis det var den eneste, der lyttede på DataAvaiable, så er DataAvaiable nu Null.

Windows skifter tilbage til baggrundstråden, der er nået til at skulle udføre
            DataAvaiable(this, e);
... det giver en NullReferenceException, da DataAvaiable jo er Null.

Løsningen:

void OnDataAvaiable(string data)
    {
        DataAvaiableEventArgs e = new DataAvaiableEventArgs() { Data = data };
        EventHandler<DataAvaiableEventArgs> d = DataAvailable;
        if (d != null)
            d(this, e);
    }

Du tager en kopi af DataAvailable over i 'd'.

Hvis nogle udfører en
    serclass.DataAvailable -= serclass_DataAvailable;

mellem
        if (d != null)
og
            d(this, e);

så er DataAvailable godt nok Null, men d er uberørt, og
            d(this, e);

fejler ikke.


Eksemplet viser lidt om hvorfor multitrådet programmering er så satans svært - selv i noget så simpelt som at fyre en event. Det forklarer noget om, hvorfor Intel, Microsoft og mange andre prøver på at finde på noget, der gør det lettere for udviklere at skrive fejlfrie multitrådede programmer.

/Klaus
Avatar billede koppelgaard Praktikant
19. oktober 2009 - 09:24 #7
Hold da kæft en flot forklaring!
Og jeg tror at noget er begyndt at sive ind selv ved mig.

Jeg prøver lige at udføre resten af dine forbedringer og vender tilbage...

Har du en kommentar til buzzzz spekulationer :"Hvis der er flere der lytter, er det så samme EventArgs object de alle får ?"

/Michael
Avatar billede kza Nybegynder
19. oktober 2009 - 09:36 #8
Hvorfor er det forresten ikke godt, at have referencer begge veje?

Nogle gange er det også OK. Hvis vi i .NET Framework indsætter en Control på en Form, så har Form en reference til Control via Form.Controls listen, og Control har en reference til Form, som vi kan få fat i i Control.Parent propertien.

Og sådan skal det være. Hvis man står med en Control, er det på sin plads at kunne sige: Giv mig den Form, den er indsat i.

Men det skaber en meget tæt kobling mellem to klasser, som gør det sværere at ændre eller udvide.

Din Sartorius klasse har ansvaret for at lytte på COM porten, behandle de indkomne data, og sige til, når der er kommet noget interessant.
Hvorfor skal den kobles til KsatClass? Med en eventmekanisme på plads er der ingen grund til, at den ved noget om, hvem der bruger de data, den stiller til rådighed.

Lad os sige at dit program bliver taget i brug, og så er der nogen der siger: "Det er helt fint, men kan du ikke også godt lige skrive måledataene ned i en database?"

Du kan gå ind i KsatClass tilføje en masse database-kode og så skrive data til databasen i KsatClass.DataToGrid().

Hmmm... måske skal DataToGrid() hedde noget andet...

Eller også hedder DataToGrid() det helt rigtige, og KsatClass skal ikke mudres til med databasekode. Og i øvrigt så risikerer man bare at introducere nye fejl, når man ændre i noget, der allerede virker.

Så i stedet laver du en helt ny klasse parallelt med KsatClass, der kunne hedde KsatDbClass. KsatDbClass oprettes og sættes til at lytte på Sartorius.DataAvaiable. Når eventen fyres skriver den data til databasen.

Du undgår at mudre din databasekode og GUI-kode sammen, og du kan tilføje helt ny funktionalitet uden at skulle ændre i kode, der allerede er testet og i orden.

/Klaus
Avatar billede kza Nybegynder
19. oktober 2009 - 09:54 #9
Hvis der er flere der lytter på en event, er det præcist det samme EventArgs objekt, de får.

og når d(this, e) returnerer, er det det samme EventArgs objekt, du står med.

Det udnyttes fx. med FormClosingEventArgs klassen, der nedarver fra CancelEventArgs, der har en bool Cancel property (set og get).

Formen fyrer en FormClosing event, når den er ved at lukke. Når 'eventen en færdig' kan formen kigge på e.Cancel og se om der var en eventhandler der satte e.Cancel til true. Hvis der var det, lader formen være med at lukke sig selv.

Man kan udnytte dette på mange forskellige måder. Man skal bare være opmærksom på, at hvis der er flere eventhandlere, så må man aldrig antage noget om, hvilken rækkefølge de bliver kaldt i.

/Klaus
Avatar billede koppelgaard Praktikant
19. oktober 2009 - 15:48 #10
Aha det lysner med dine forklaringer - specielt dine overvejelser i #8

Angående #9:
Fandt lige eventen frem:

private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
        {

        }
Hvordan kan man afgøre, hvem der satte e.Cancel til true?
Kan man aflæse det af sender?
Avatar billede kza Nybegynder
19. oktober 2009 - 22:52 #11
Det kan godt være mit eksempel var mere forvirrende end forklarende.

Du skal nærmere forestille sig, at en af Microsoft drengene i Form klassen i .NET Framework har skrevet noget a la:

public class Form
{
    ...
    public event EventHandler<FormClosingEventArgs> FormClosing;

    ...

    protected virtual OnFormClosing(CloseReason reason)
    {
        FormClosingEventArgs e = new FormClosingEventArgs(reason, false);

        Eventhandler<FormClosingArgs> d = FormClosing;
        if (d != null)
        {
                d(this, e);
                if (e.Cancel)
                {
                    // Formen skal ikke lukkes alligevel
                    ...
                }
                else
                {
                    // Formen skal lukkes
                    ...
                }
        }
        else
        {
            // Formen skal lukkes
            ...
        }
    }
}

Vi kan faktisk godt antage, at de har brugt sammen 'opskrift' på at lave deres events, som jeg beskrev ovenfor.

OnFormClosing(reason) vil blive kaldt, når der er sket et eller andet, der får formen til at lukke - måske har nogen kaldt Form.Close().

Når vi når til linien
    if (e.Cancel)
så har eventen været fyret i linien forinden, og alle eventhandlere - hvis der var nogen - er blevet kaldt. Hvis en at disse handlere har sat e.Cancel til true, så skal vi nu afbryde lukningen af formen.

Vi ved ikke hvem, der har sat e.Cancel til true - og vi er faktisk også ligeglade.


Det var egentlig bare for at sige at EventArgs kan bruges som en slags 2-vejs kommunikation. Klassen, der fyrer eventen, kan lægge data i EventArgs, som den der lytter på eventen kan kigge på.
Men man kan også lave en EventArgs klasse, så dem der lytter på eventen kan give oplysninger tilbage til klassen, der fyrede eventen.
Avatar billede koppelgaard Praktikant
20. oktober 2009 - 10:54 #12
Tak for svart. Jeg tror jeg forstår - næsten i hvert fald.
Nu tror jeg snart, jeg er nået til ende med alt det, jeg er i tvivl om ved dette projekt.

Der er dog lige endnu 2 spørgsmål:
Jeg skal have overvåget om vægten, som kommer fra Sartorius overstiger 2500.
Hvis det er tilfældet skal en magnetventil åbnes.
Jeg regner med at lave en klasse, der styre magnetventiler.
Og så tænkte jeg, at jeg kunne overvåge at vægten ikke overstiger 2500 fra
void serclass_DataAvailable(object sender, DataAvaiableEventArgs e)
        {
            DataToGrid(e.Data);
            If(e.data>2500) Valve.Open
        }

Det vil kræve at Ksatklassen får kendskab til ventilklassen:
public KsatClass(ZedView zv, Sartorius serclass,ValveClass,Timer timer)
Kunne godt tænke mig at visualiserer at ventilen er åben i FormMain f.eks. med en  RadioButtom.
Men hvordan kringler man lige det? Det vil kræve en event eller at Ksat får FormMain med i constructoren ikke?

Næste spørgsmål:
Data der opsamles i de 5 grid skal en efter en flyttes over i en anden form også indeholdende et ZedgrafControl og en DataGridView for at redigeres inden de gemmes.
De to controller skal kontrollers af en zedView.
Regnede med at aktivere en metode i Ksat, som åbner en ny form med DataTable som argument.
Data fra DataTable flyttes over i et nyt DataTable som fungerer som source til datawievet
Årsagen til at data flyttes er at undgå at berøre det oprindelige datatable.
Data i dataViewet regigeres, herved ændres data i DataTable
Data fra DataTable gemmes på disk.
Vi denne struktur være ok?
Avatar billede kza Nybegynder
20. oktober 2009 - 13:44 #13
Du er blevet sat i gang med at refaktorerer din kode. Dvs. at ændre på koden uden at ændrer på systemets funktionalitet. Der er mange grunde til at refaktorerer: Gøre det lettere at vedligeholde systemet, dvs. ændre udvide eller rette i funktionalitet. Gøre koden mere læselig. Gøre det muligt at teste koden, med testværktøjer. Etc. Etc.

Hvis man afsætter halvdelen af sin tid til at implementerer funktionalitet og halvdelen til at refaktorerer, så er man meget godt kørende. (Men det kan desværre ikke altid lade sig gøre.)

Men funktionaliteten skal selvfølgelig også være på plads. Du skrev at din opdatering af griden er langsom. Der er ikke noget i din kode, der bør tage mere end nogle millisekunder, så find ud af, hvad der foregår - hvor forsvinder tiden?


Der findes et princip, der hedder Single Responsibility Principle (SRP) - det er et princip ikke en regel! Det siger, at en klasse kun skal have ansvaret for en ting. Det kan sættes lig med, at der kun må være en grund til, at en klasse skal ændres.

Jeg synes ikke at du skal blande ventilstyring ind i KsatClass. Lav en ValveController klasse (vær ikke bange for at lave mange klasser), der ligner KsatClass med constructor'en:
    public ValveController(CheckBox alarmCheckBox, ValveClass valve, Sartorius serclass);

ValveController lytter på Sartorius.DataAvaiable, hvis der modtages en vægt større end 2500, kaldes
    valve.Open();
    alarmCheckBox.Checked = true;

(Husk på at du skal skifte til forgrundstråden, inden du rører ved alarmCheckBox).


Kan dette design forbedres (med SRP i baghovedet)?
ValveController har ansvaret for at reagere når vægten er for høj, den bør ikke også have ansvaret for hvordan det rapporteres på GUI'en.

Du kunne indføre en ValveAlarmView klasse:
class ValveAlarmView
{
    CheckBox checkBox;
    public ValveAlarmView(CheckBox checkBox)
    {
        this.checkBox = checkBox:
    }

    public void SetAlarmState(bool alarm)
    {
        checkbox.Checked = alarm   
    }
}

som ValveController så får med i construtor'en i stedet for en CheckBox.

Hvis du fx. beslutter, at alarmen i stedet skal vises med en label med en rød, fed tekst i, så er det ValveAlarmView, der skal ændres - ikke ValveController. ValveController skal kun ændres, hvis reaktionen på en for høj vægt skal ændres.

Men som sagt er SRP et princip - ikke en regel. Jeg tror ikke, jeg ville indføre en ValveAlarmView klasse, med mindre 'GUI rapporteringen' bliver mere kompliceret eller tit ændrer sig. (De vil have en CheckBox, så vil de have en tekst i en Label, så vil de have en popup messagebox. Og så når de har tænkt lidt over det, så vil de vist alligevel gerne bare have CheckBox.)


Lige et spørgsmål om dine Timer'e:

Er det rigtig forstået at du kalder Sartorius.SendCommand() for at få en SerialPort.DataReceived event - for at få en aflæsning?
Hvis port.Write(command) er lang tid om at udføre, fordi den står og venter på porten, så kan det være, det er her, der forsvinder tid. (Det kan undersøges ved at tage nogle målinger.)

Jeg synes i øvrigt ikke, at det er KsatClass, der skal være ansvarlig for, at porten polles. Jeg synes at Sartorius klassen skal indkapsle den serielle port, og timeren skal derfor ligge i Sartorius klassen.

Jeg synes faktisk også at Sartorius klassen skal have lov til at oprette timeren i dens constructor:
    public Sartorius(...)
    {
        ...
        timer = new System.Timers.Timer()
        this.timer.Tick += new System.EventHandler(this.timer_Tick);
        ...
    }

    private void timer_Tick(object sender, EventArgs e)
    {
        SendCommand();                       
    }


da du gerne vil kunne styre din timer fra FormMain kan du tilføje en property til Sartorius
    public Timer Timer
    { get { return timer; } }

og lave din property i KsatClass om:
    public Timer Timer
    { get { return serclass.Timer; } }

Bemærk, at en Timer, du trækker ind på en Form i Visual Studio, er af System.Windows.Forms.Timer klassen, mens jeg oprettede en timer af System.Timers.Timer klassen. (Der findes faktisk også en System.Threading.Timer klasse i .NET Framework). Tre forskellige klasser med samme navn - det kan godt skabe lidt forvirring.

Når System.Windows.Forms.Timer afgiver et tick og kalder timer_Tick, så sker det altid på forgrundstrådet. Når System.Timers.Timer kalder timer_Tick, så sker det på en baggrundstråd!
I dit tilfælde er det en fordel. Hvis SendCommand() tager lang tid, så blokerer den nu ikke længere for din forgrundstråd, og du har måske fået løst dit performance problem?

/Klaus
Avatar billede koppelgaard Praktikant
20. oktober 2009 - 21:26 #14
Aha det lyder alt sammen interessant. F.eks. det med timertyper, som du nævnte sidst. Viste godt at der var flere typer men ikke at der var de beskrevne forskelle.
Og dine forklaringer SRP.
Jeg skal have tykket på og leget med det.
Min tid til dette projekt er, desværre ikke ret stor per dag. Det bliver kun en lille bid, jeg når hver dag.
Men jeg er meget glad for dine store forklare lyst. Den er guld værd for mig. Ikke kun for dette projekt. Lige så meget for det, jeg gerne skulle til at lave i fremtiden.
Og her får jeg brug for hjælp igen - og meget gerne din hjælp.
Du er sørme god til at forklare. Men du er sjældent på Eksperten.
Dvs. at jeg skal være heldig, hvis jeg skal vække din opmærksomhed. Kan der tænkes en løsning på det?
Vil du for resten snart have nogle point? Nogen samler, men jeg har på fornemmelsen at point blot er en detalje for dig?

/Michael
Avatar billede koppelgaard Praktikant
21. oktober 2009 - 21:44 #15
Jeg synes det lyder fornuftigt at flytte ansvaret for timer til sartorius. Jeg har derfor fulgt dig råd og flyttet ansvaret dertil og endvidere skiftet type som beskrevet.

Endvidere har jeg logget timer.
Det ser ikke ud til at der er noget performance problem med een port i gang.
Men se her:

21:27:58:312:SendCommand, before send
21:27:58:328:SendCommand, after send
21:27:58:531:data ready
21:27:58:546:DataToGrid, before Invoke
21:27:58:593:DataToGrid, before Invoke
21:27:58:640:DataToGrid, After Invoke
21:27:58:687:DataToGrid, After Invoke
21:28:03:312:SendCommand, before send
21:28:03:375:SendCommand, after send
21:28:03:562:data ready
21:28:03:578:DataToGrid, before Invoke
21:28:03:625:DataToGrid, before Invoke
21:28:03:640:DataToGrid, After Invoke
21:28:03:687:DataToGrid, After Invoke
21:28:08:312:SendCommand, before send
21:28:08:312:SendCommand, after send
21:28:08:515:data ready
21:28:08:531:DataToGrid, before Invoke
21:28:08:546:DataToGrid, before Invoke
21:28:08:609:DataToGrid, After Invoke
21:28:08:656:DataToGrid, After Invoke
21:28:13:312:SendCommand, before send
21:28:13:328:SendCommand, after send
21:28:13:515:data ready
21:28:13:531:DataToGrid, before Invoke
21:28:13:562:DataToGrid, before Invoke
21:28:13:578:DataToGrid, After Invoke
21:28:13:640:DataToGrid, After Invoke
21:28:18:312:SendCommand, before send
21:28:18:328:SendCommand, after send
21:28:18:515:data ready
21:28:18:531:DataToGrid, before Invoke
21:28:18:546:DataToGrid, before Invoke
21:28:18:578:DataToGrid, After Invoke
21:28:18:625:DataToGrid, After Invoke
Det ser ud til at hvert modtaget data(data ready) sættes to gange ind i griddet???
Meget intersant faktisk.
Men hvorfor mon det??

Og så er jeg begyndt at logge exception. Der kommer lidt skrald ud som jeg lige skal have set på.
Avatar billede kza Nybegynder
22. oktober 2009 - 00:27 #16
Tid er altid en begrænsning. Man kan næsten altid blive ved med at refaktorerer og forbedre sit design, så det er om at finde en
balance.

SRP er del af en trend, der går mod mindre klasser og mindre metoder - men så tilgengæld mange flere af dem. Nogle snakker om, at en metode helst aldrig skal være på mere end 6-8 linier. Nogle er indædte modstandere af denne trend. Det man kan/bør gøre, er at prøve ideerne af - lege med dem (som du selv skriver) og finde ud af, hvad man synes.

Dit trace viser, at der ikke er noget, der tager tid af betydning.
De to kald til DataToGrid viser også at du har en fejl.
Hvis du kommer til at kalde
    serclass.DataAvailable += serclass_DataAvailable;
to gange, så ville det være en forklaring.


Og nej, jeg samler ikke på points. Men det kan selvfølgelig være meget godt at have lidt at give af, når jeg vil stille spørgsmål.
Så du må da gerne give min points for den tidligere tråd - jeg mener, jeg lagde et svar ind i den.


Man kan vel altid få min opmærksomhed ved at gå ind på min profilside og sende en besked. Jeg er freelancer og har lige afsluttet en stor opgave og er ved at afslutte en mindre opgave, så lige nu har jeg mere tid til rådighed, end jeg plejer at have.
Det er ikke til at sige, hvornår det ændrer sig.

/Klaus

P.S. Der er et råd, jeg ikke har givet dig, men som andre sikkert ville have givet dig, som det første.
Det er UNIT TEST.
Altså at lave et separat projekt, hvor du skriver kode, der tester koden i dit 'rigtige' projekt.
Det kræver lige lidt ekstra. Man skal sætte sig ind i, hvordan man laver sådan nogle tests, og man kommer til at lave sin kode om, så den bliver testbar. Det er ikke helt simpelt.
Men det er vejen frem - det er der ingen tvivl om. Så på et eller andet tidspunkt, skal du sætte dig ind i det.
Avatar billede koppelgaard Praktikant
22. oktober 2009 - 16:47 #17
Har hørt om UNIT TEST for lang tid siden og tænkte at det blev jeg måske nok nød til at sætte mig ind i en gang.
Og den gang er måske ved at være.

Nu har jeg afgive 100 for sidste spørgsmål http://www.eksperten.dk/spm/889431
Men det gik på at få et datagridview til at opdatere.
Det fik du 100 for.

Så bevægede snakken sig hen imod optimering af kode og derfor oprettede jeg det nye spørgsmål: strømligning af kode.

Her har du ydet en enorm indsats efter min mening, så det er værd 180 point og så de sidste 20 til Buzzz som også lige var inde over.

SÅ sender I svar?

Michael
Avatar billede Syska Mester
22. oktober 2009 - 16:56 #18
Jeg har ikke rigtigt bidraget med noget specielt, så jeg synes du selv skal beholde dem, hvis Klaus ikke samler ...

Jeg har også lært noget af det her, specielt det med at fyre eventen over i en midtidig variable og derefter kontrollere omd en er null ... men dvs at Event er en value type ... hvilket jeg faktisk ik' troede det var.

mvh
Avatar billede koppelgaard Praktikant
22. oktober 2009 - 17:06 #19
Nååå det er ikke kun mig der lære.
Det var da rart !!
Så føler jeg mig ikke helt så dum.
Avatar billede kza Nybegynder
23. oktober 2009 - 11:12 #20
buzzzz: Jeg skrev det med den midlertidige variabel fordi, det har jeg en gang lært:
Jeffrey Richter: CLR Via C#: Applied .NET Framework 2.0 Programming.

Jeg har også fundet et eksempel her
MSDN eksempel.

Da jeg så skulle forklare det, dukkede det samme op i mit hoved: For at det skal virke skal event være en ValueType - hvilket på en eller anden måde er lidt overraskende.

En event er faktisk bare en Delegate med noget 'syntactic sugar'. Jeg har slået Delegate op i Object Browser - og den nedarver ikke fra ValueType.

event er altså ikke en ValueType. Hvorfor virker det så alligevel?
Avatar billede kza Nybegynder
23. oktober 2009 - 11:42 #21
Jeg har ikke fundet svaret på det med eventen, men jeg fandt en anden interessant måde at gøre det på. For at bruge samme eksempel:

    public event EventHandler<DataAvaiableEventArgs> DataAvailable = delegate { };

    void OnDataAvaiable(string data)
    {
        DataAvaiableEventArgs e = new DataAvaiableEventArgs() { Data = data };
        DataAvailable(this, e);
    }

= delegate {} tilføjer en tom delegate - eller man kan sige, at man tilføjer en eventhandler, der ikke gør noget.
Dvs at der altid er en hanlder, der lytter på eventen, og DataAvaiable kan ikke blive null. Vi kan derfor fjerne tjekket fra OnDataAvaiable.

Her er der en diskussion om, at det ikke er gratis at kalde en tom eventhandler, men med mindre man er ude i at skulle klemme de sidste nanosekunder ud at sit program, så er det ikke noget problem.

/Klaus
Avatar billede koppelgaard Praktikant
24. oktober 2009 - 09:34 #22
Nej hvor smart!
Michael
Avatar billede Syska Mester
24. oktober 2009 - 12:40 #23
Cool, så var mit hoved ikke helt i grøften den dag.

Nej, måske kun i server apps hvor man gerne vil have alt performance man kan få, så dekstop apps har du nok ret, som de også skriver på den side. Alt koster, bare et spm om hvor meget :-)

Overstående gør koden nemmere at læse, men spørgsmålet er om man altid vil huske at tilføje sin tomme delegate ... det kan jo også smutte ...

// ouT
Avatar billede kza Nybegynder
28. oktober 2009 - 22:14 #24
Et svar
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