Avatar billede microjet Nybegynder
26. september 2008 - 14:06 Der er 28 kommentarer og
1 løsning

Problem med tilgang til metoder i ClassLibrary (DLL-fil)

Jeg har længe bakset med at få tilgang til nedenstående to metoder. Jeg har brug for et eksempel der viser hvordan jeg i en simpel windows form application får vist teksterne "Hello1" og Hello2" i en Richtextbox, når der trykkes på en knap.

Skal tilgangen til metoderne erklæres i selve dll'en (hvilket er at foretrække)eller i applicationen

Hvis jeg putter NamespaceClassLibrary i using, kan jeg skrive
NamespaceClassLibrary.Namespace1.Class1...og så ikke mere!!

skulle gerne se sådan her ud:

NamespaceClassLibrary.Namespace1.Class1.Method1()
                                      .Method2()
                                      ....osv.
                                     
NamespaceClassLibrary.Namespace1.Namespace2.Class2.Method2()
                                                  .Method3()
                                                  ....osv.
                                         
Der er 200 point til den der kommer med et godt eksempel, somjeg kan bruge til at komme videre!


(NamespaceClassLibrary.dll)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Namespace1
{
    public class Class1
    {
        public void Method1(string str1)
        {
            str1 = "Hello1";
        }
    }

    namespace Namespace2
    {
        public class Class2
        {
            public void Method2(string str1)
            {
                str1 = "Hello2"; 
            }
        }

    }

}
Avatar billede aaberg Nybegynder
26. september 2008 - 14:11 #1
Du bliver nød til at oprette en instans af din klasse først.

Namespace1.Class1 myClass = new Namespace1.Class1();

myClass.Method1("test");
Avatar billede aaberg Nybegynder
26. september 2008 - 14:12 #2
eller:

Namespace2.Class2 myOtherObject = new Namespace2.Class2();
myOtherObject.Method2("hello world");
Avatar billede microjet Nybegynder
26. september 2008 - 15:03 #3
Skal jeg gøre det i ClassLibrary'et (dll-filen) eller skal det gøres i selve windows form applicationen?

Jeg var jo ude efter et mere fyldestgørende eksempel hvis det var muligt... som beskrevet oven for... men tak indtil vidre.
Avatar billede microjet Nybegynder
26. september 2008 - 15:11 #4
Som beskrevet er jeg ude efter at få en punktumnotation som letter brugen af DLL-filen for programøren

Når du én gang har tilføjet

using System

Så kan du skrive:

System.Byte.Parse()
System.Byte.TryParse()
System.Byte.????

System.Char.IsDigit()
System.Char.IsLetter()
System.Char.????

Og det er præcis det jeg også vil med min dll!
Avatar billede pidgeot Nybegynder
26. september 2008 - 15:18 #5
Nej, det kan du gøre *uden* using System. Med using System kan du skrive:

Byte.Parse()
Byte.TryParse()

etc.

Du kommer under ingen omstændigheder udenom at med mindre du har en instans af din klasse, så kan du kun tilgå statiske (static) metoder. Da du ikke har nogle af dem, er det helt naturligt at du ikke kan komme videre - for du kigger på selve klassen, ikke en instans af den.

Hvad der er bedst i din situation kan der ikke gives et fast svar på, men som tommelfingerregel kan man sige at en metode bør være statisk, hvis den ikke er afhængig af nogen form for interne tilstande. Ellers skal man have fat i en instans først (ved at bruge new).
Avatar billede microjet Nybegynder
26. september 2008 - 15:31 #6
OK! så de skal være statiske!

Dvs. Parse() og TryParse() begge må være statiske!

Det kan da være det bare er det der skal til!
Avatar billede aaberg Nybegynder
26. september 2008 - 15:45 #7
prøv

namespace Namespace1
{
    public static class Class1
    {
        public static void Method1(string str1)
        {
            str1 = "Hello1";
        }
    }

    public static class Class2
    {
        public static void Method2(string str1)
        {
            str1 = "Hello2";
        }
    }
}
Avatar billede aaberg Nybegynder
26. september 2008 - 15:47 #8
Når en metode er statisk ligger den på klassen. F.eks. int.Parse(). Når en metode IKKE er statisk, ligger den på objeketet. F.eks. myString.SubString()
Avatar billede microjet Nybegynder
30. september 2008 - 12:42 #9
OK! Det er rigtigt at det virker med punktumnotationen når metoderne er statiske, men jeg har altså brug for at det er et objekt, som jeg kan oprette flere instanser af.

Men der må da være en måde at få punktumnotationen til at virke selv om det er et object!

Hvis vi tager udgangspunkt i mit eks:

Namespace1.Namespace2.Class2.method2()

Jeg håber virkelig nogle kan hjælpe!!!
Avatar billede pidgeot Nybegynder
30. september 2008 - 12:52 #10
Jamen, du har jo fået svaret. Hvis metoden ikke er statisk, så kommer du ikke udenom at oprette en instans, og dermed kan du ikke bare chaine det hele vejen - det er ganske enkelt umuligt, og det giver heller ikke mening - for hvis metoden ikke kan gøres statisk, så må det betyde at du arbejder med nogle instansvariable. Hvis du kunne køre sådan en metode uden en instans, hvor skulle den så lige finde de instansvariable henne? *-)

Husk nu at en metode godt kan være statisk uden at det forhindrer en i at have instanser af klassen. Du kan bare ikke manipulere med ikke-statiske klassevariable fra de statiske metoder.
Avatar billede aaberg Nybegynder
30. september 2008 - 13:07 #11
Der er et designpattern der hedder "Singleton pattern", som går ud på at have en instans a en klasse liggende statisk på klassen. Det kan være det er dette du leder efter?
http://en.wikipedia.org/wiki/Singleton_pattern

Noget helt ander er at, du kan godt have en statisk metode på en ikke-statisk klasse. Hvis du f.eks. tager System.String klassen i frameworket. Dette er en ikke-statisk klasse, som du kan oprette instanser af. Men den har alligevel flere statiske metoder, som f.eks. System.String.Format().
Avatar billede microjet Nybegynder
30. september 2008 - 16:51 #12
Jeg har programmeret Delphi i en del år, men det her C# er lidt nyt for mig, så bær over med mig hvis jeg lyder grøn, for det er jeg ;-)

Denne dll sender blot en kommando... en anden dll sørger for at sende svaret på kommandoen retur til applicationen.

Jeg har nu prøvet at lave det med statiske metoder, men det driller stadig, så nu har jeg lige skåret 5000 linier væk så der kun er 2 kommandoer, måske er det bedre for jer at studerer hvad jeg egentlig sidder og roder med.

Jeg har problemer med at få adgang til HBMux, som er er object... Hvis jeg opretter objektet i selve metoden brokker den sig ikke, men jeg skal kun oprette HBMux én gang

Håber nedenstående kan hjælpe lidt

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MultiplexClassLibrary;


    namespace DeviceClassLibrary
    {
       
        public class HBDataPacket
        {
            //Interface selection
            public bool Enable_COMPortDevice;
            public bool Enable_CANbusDevice;
            public bool Enable_ProfibusDevice;
            public bool Enable_MODBusDevice;

            //COMport settings
            public string COMStartStr;
            public string COMEndStr;
            public string COMTxStr;
            public string COMname;
            public int COMBaudrate;
            public bool IncludeStartStr;

            //CAN settings
            public string CANopen;

            //Profi settings
            public string ProfiBus;

            //MODbus settings
            public string Modbus;
            //Common settings
            public int Max;
            public int Min;
            public string Unit;

            public bool Read;
            public bool Write;
            public int ReTransmit;
            public int Timeout;
            public bool Error;
            public string ErrorMgs;

            public DateTime TxTimeStamp;
            public int State;
            public string ReturnAnswer;
        }




        public class HBDevice
        {
                                                 
          //******MBMultiplex kommer fra DLL'en  MultiplexClassLibrary
            HBMultiplex HBMux = new HBMultiplex();
                                                  //******Hvor skal HBMux oprettes***

            public static bool _COMPortDevice = false;
            public static bool _CANbusDevice = false;
            public static bool _ProfibusDevice = false;
            public static bool _MODBusDevice = false;
            public static bool _HBUSBdevice = false;
            public static string _COMname;
            public static int _COMBaudrate;




            //Constructor for DeviceClassLibrary
            public HBDevice()
            {
                HBMux.NewMultiplexData += new HBMultiplex.MultiplexDataDelegate(DeviceDataRecieved);
            }


            //delegate til event
            public delegate void DeviceDataDelegate(HBDataPacket datapacket);
         
            // event af typen ComportDataDelegate
            public event DeviceDataDelegate NewDeviceData;
         



            // Hvis der er abonnenter på eventen, kaldes den.
            private void OnDeviceData(HBDataPacket datapacket)       
            {
                if (NewDeviceData != null)
                    NewDeviceData(datapacket);
            }



            public void DeviceDataRecieved(HBDataPacket datapacket)
            {
                OnDeviceData(datapacket);
            }


            public static class Commands
            {
                public static void _ID_GetDeviceIdentification()
                {
                    HBDataPacket datapacket = new HBDataPacket();

                    datapacket.Enable_COMPortDevice = _COMPortDevice;
                    datapacket.Enable_CANbusDevice = _CANbusDevice;
                    datapacket.Enable_ProfibusDevice = _ProfibusDevice;
                    datapacket.Enable_MODBusDevice = _MODBusDevice;
                    datapacket.COMname = _COMname;
                    datapacket.COMBaudrate = _COMBaudrate;

                    datapacket.Max = 0;
                    datapacket.Min = 0;
                    datapacket.Unit = "";

                    datapacket.COMTxStr = "ID\r";
                    datapacket.COMStartStr = "D:";
                    datapacket.COMEndStr = "\r";
                    datapacket.IncludeStartStr = false;

                    datapacket.CANopen = "SDO2900sub08";

                    datapacket.ProfiBus = "read 21,0";

                    datapacket.Modbus = "202C,2";

                    datapacket.Timeout = 100;
                    datapacket.Read = true;
                    datapacket.Write = false;
                    datapacket.ReTransmit = 0;
                    datapacket.State = 0;
                    datapacket.TxTimeStamp = DateTime.Now;
                    datapacket.ErrorMgs = "";

                    HBMux.MultiplexDataSend(datapacket); //FEJLER på HBMux**********
                }



                public static void _FL_SetFilterValue(string data)
                {
                    HBDataPacket datapacket = new HBDataPacket();

                    datapacket.Enable_COMPortDevice = _COMPortDevice;
                    datapacket.Enable_CANbusDevice = _CANbusDevice;
                    datapacket.Enable_ProfibusDevice = _ProfibusDevice;
                    datapacket.Enable_MODBusDevice = _MODBusDevice;
                    datapacket.COMname = _COMname;
                    datapacket.COMBaudrate = _COMBaudrate;

                    datapacket.Max = 23;
                    datapacket.Min = 0;
                    datapacket.Unit = "";

                    datapacket.COMTxStr = "FL" + data + "\r";
                    datapacket.COMStartStr = "O";
                    datapacket.COMEndStr = "\r";
                    datapacket.IncludeStartStr = true;

                    datapacket.CANopen = "SDO2100sub04";

                    datapacket.ProfiBus = "read 18,04 write 31,04";

                    datapacket.Modbus = "2106,2";

                    datapacket.Timeout = 100;
                    datapacket.Read = true;
                    datapacket.Write = true;
                    datapacket.ReTransmit = 0;
                    datapacket.State = 0;
                    datapacket.TxTimeStamp = DateTime.Now;
                    datapacket.ErrorMgs = "";

                    HBMux.MultiplexDataSend(datapacket);//FEJLER på HBMux**********
                }

            }

        }

    }
Avatar billede aaberg Nybegynder
30. september 2008 - 18:45 #13
Du kan oprette en statisk klasse, som indeholder en instans af HPMux klassen. Hvis vi vil have den punktum notation du ønsker, kan vi kalde klassen for MultiplexClassLibrary

public static class MultiplexClassLibrary
{
  private static HBMux _hBMux;
  public static HBMux HBMux
  {
      get
      {
        if (_hBMux == null)
            _hBMux = new HBMux();

        return __hBMux;
      }
  }
}

Nu kan du få fat i koden overalt ved at skrive:

MultiplexClassLibrary.HBMux

For at kalde MultiplexDataSend:
MultiplexClassLibrary.HBMux.MultiplexDataSend(datapacket);

Hvis du gør det på denne måde, bliver instansen automatisk oprettet første gang du bruger den. Derfor skal du ikke selv oprette en instans i din kode. Du skal altså fjerne denne linje fra koden du postet:
HBMultiplex HBMux = new HBMultiplex();
Avatar billede aaberg Nybegynder
30. september 2008 - 20:46 #14
En lille rettelse til den statiske klasse:

public static class MultiplexClassLibrary
{
  private static HBMux _hBMux;
  public static HBMux HBMux
  {
      get
      {
        if (_hBMux == null)
            _hBMux = new HBMux();

        return _hBMux;
      }
  }
}
Avatar billede microjet Nybegynder
01. oktober 2008 - 09:47 #15
Hmmm... Hvor kommer HBMultiplex ind i billede? HBMux er jo en instans af klassen HBMultiplex, som ligger i DLL filen MultiplexClassLibrary.

Du siger jo jeg ikke skal bibeholde:
HBMultiplex HBMux = new HBMultiplex();
Avatar billede microjet Nybegynder
01. oktober 2008 - 10:14 #16
Er det ikke sådan her du mener... altså HBMultiplex istedet for HBMux

            public static class MultiplexClassLibrary

            {
                private static HBMultiplex _hBMux;
                public static HBMultiplex HBMux
                {
                    get
                    {
                        if (_hBMux == null)
                            _hBMux = new HBMultiplex();

                        return _hBMux;
                    }
                }
            }

Vil ovenstående kode så kun oprette objektet HBMultiplex én gang?
Avatar billede aaberg Nybegynder
01. oktober 2008 - 10:30 #17
Du har selvfølgelig helt ret. Jeg mener HBMultiplex!

Og, ja. Ovenstående kode opretter en instans af klassen første gang du bruger den via den statiske property. Altså første gang du skriver:

MultiplexClassLibrary.HBMux.EtEllerAndet().

Alle andre gange du kalder propertien, er det den samme instans der bliver brugt. På denne måde bliver instansen bare oprettet en gang, så længe programmet er åbent.
Avatar billede microjet Nybegynder
01. oktober 2008 - 10:37 #18
OK! Tusind tak indtil videre jeg prøver det lige og vender tilbage!
Avatar billede microjet Nybegynder
01. oktober 2008 - 14:17 #19
Jeg har fået det til at virke... sådan da!

Men hvordan kan jeg oprette flere instanser af ovenstående "DeviceClassLibrary" fra min application... Før lavede jeg det sådan her:

DeviceClassLibrary.HBDevice.Commands Device2 =
new DeviceClassLibrary.HBDevice.Commands();

Men det kan jeg jo ikke nu, så hvordan gør jeg så det?


Her et dump af min test application

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DeviceClassLibrary;


namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        DeviceClassLibrary.HBDevice.Commands Device2 = new DeviceClassLibrary.HBDevice.Commands();
*************Tja det har ikke den store virkning nu da metoderne er statiske*******
       
        public Form1()
        {
            InitializeComponent();
        }


        delegate void testdelegate(HBDataPacket datapacket);
        public void test(HBDataPacket datapacket)
        {
            if (this.richTextBox1.InvokeRequired)
            {
                testdelegate d = new testdelegate(test);
                this.Invoke(d, new object[] { datapacket });
            }
            else
            {     
                this.richTextBox1.AppendText(datapacket.ReturnAnswer);
            }
        }


        private void Form1_Load_1(object sender, EventArgs e)
        {
            Device1.NewDeviceData += new HBDevice.DeviceDataDelegate(test);
        }       


        private void button1_Click(object sender, EventArgs e)

        {
            DeviceClassLibrary.HBDevice.Commands._ID_GetDeviceIdentification();
        }

      private void button2_Click(object sender, EventArgs e)
      {
          richTextBox1.Clear();
      }



      private void Form1_FormClosing(object sender, FormClosingEventArgs e)
      {
          //nød til at stoppe invoke eller abonnement på device delegate
          Device1.NewDeviceData -= new HBDevice.DeviceDataDelegate(test);
      }

    }
}
Avatar billede pidgeot Nybegynder
01. oktober 2008 - 15:01 #20
Nu må du altså beslutte dig. Det er dig selv der har sagt at du vil kunne skrive alt hele vejen fra namespace til metodekald (hvorfor du altså ikke har nogen instans at køre på), men samtidigt vil du alligevel oprette instanser. Det giver ikke mening - for enten skal en metode bruge en instans, eller også skal den ikke.

I dit tilfælde skal du vel vide hvilken port der benyttes. Så har du behov for en instans (for du kan jo have gang i flere porte på samme tid), og så duer det ikke at du har metoder der skal kende den oplysning, men samtidigt ikke må få en instans at kigge på - for den oplysning er jo netop instans-afhængig.
Avatar billede microjet Nybegynder
01. oktober 2008 - 15:21 #21
Ja men jeg er lidt forvirret...aaberg skrev:

"Noget helt ander er at, du kan godt have en statisk metode på en ikke-statisk klasse. Hvis du f.eks. tager System.String klassen i frameworket. Dette er en ikke-statisk klasse, som du kan oprette instanser af. Men den har alligevel flere statiske metoder, som f.eks. System.String.Format()."

Jeg vil gerne kunne benytte min DLL flere gange samtidig i min applikation, så jeg f.eks opretter et object/instans der kommunikerer med COM1 og et andet der komunikerer med COM2... De skal jo bruge den/de samme dll'er... men det er måske ikke muligt?
Avatar billede pidgeot Nybegynder
01. oktober 2008 - 16:57 #22
Jamen, det er jo bare et spørgsmål om at oprette to instanser, og fortælle den ene at den skal køre på COM1, og den anden på COM2?

Hvor klassen kommer fra er komplet irrelevant for det formål.
Avatar billede microjet Nybegynder
01. oktober 2008 - 22:08 #23
Jammen! Hvis det er det der skal til, så er jeg måske ved at være ved vejs ende i min problematik.

Hvordan opretter jeg så 2 instanser af min dll: "DeviceClassLibrary.dll"

aaberg'e eksempler har været meget brugbare for mig. Jeg har brug for at få det beskrevet i et kode-eks, for at forstå helt præcis hvad du mener, for jeg er lidt ny i det her.

Så hvis en af jer kan vise hvordan jeg kan oprette 2 instanser af min "DeviceClassLibrary.dll", så vil jeg blive meget meget glad.
Avatar billede pidgeot Nybegynder
01. oktober 2008 - 22:25 #24
Jamen, hvad skal du bruge to instanser af din DLL til? *-)

Det du skal bruge er instanser af *klassen* i din DLL. Tilføj DLL'en som en reference, og så er det bare et spørgsmål om at bruge den. :)
Avatar billede microjet Nybegynder
01. oktober 2008 - 22:37 #25
Jammen så har jeg da ikke punktum-notationen.... det sidste kode eks. viser hvad jeg har gjort... er det rigtigt... som jeg skrev må du meget gerne give et eks!
Avatar billede aaberg Nybegynder
02. oktober 2008 - 08:12 #26
Hvis det du har brug for er, at oprette en ny instans af klassen og overskrive den gamle, kan du tilføje en set-metode i den statiske property:

public static class MultiplexClassLibrary
{
  private static HBMultiplex _hBMux;
  public static HBMultiplex HBMux
  {
      get
      {
        if (_hBMux == null)
            _hBMux = new HBMux();

        return __hBMux;
      }
      set
      {
        _hBMux = value;
      }
  }
}

Så vil du til hver en tid kunne 'resette' din HBMux ved at skrive:
HBMultiplexClassLibrary.HBMux = new HBMultiplex();

Dette vil overskrive referencen til objektet, så du mister referencen til det tideligere object. Hvis du har brug for at kunne tilgå flere instanser af HBMultiplex klassen på samme tid, er dette ikke løsningen.

Og en lille tilføjelse: Man kan ikke oprette instanser af en dll. I .NET er en dll et fysisk klassebibliotek. Hvad navnet på dll filen er, skal du bare tænke på imens du browser efter filen i "Add Reference". Når en dll fil er tilføjet et projekt, bliver alle namespaces og klasser i dll filen automatisk en del af det rammeverk du har til rådighed fra din kode. Punktum notationen som man har i .NET har ikke noget med dll'en at gøre, men derimod namespaces. Tager du f.eks. XmlDocument klassen, så ligger den i namespacet System.Xml. At et namespace ofte har samme navn dll filen, er et designspørgsmål. Du kan sagtens have et namespace som hedder MyCompany.Classes i en dll som hedder ClassLib.dll
Avatar billede microjet Nybegynder
02. oktober 2008 - 23:30 #27
Tak for det fine eks… det får jeg faktisk brug for i en anden sammenhæng, men lad mig lige forklare det lidt mere detaljeret.

Jeg har 3 dll’er

<DeviceClassLibrary.dll>: Indeholder alle kommandoer. Metoderne sender blot en pakke af sted. En delegate sørger for at returnere resultaterne til selve applicationen.

<MultiplexClassLibrary.dll> Vælger Interface eks: COM, LAN, CAN, ProfiBus…osv og sender pakkerne frem og tilbage. Dvs alle pakker går via denne DLL.

<ComportClassLibrary.dll> Komunikerer med COM porten… en delegate returnerer resultat til Multiplex dll’en og videre tilbage til DeviceClassLibrary.

Senere kommer der så også en LANClassLibrary.dll, en CANClassLibrary.dll... osv

Jeg ville gerne have at programøren blot kunnen tilføje <DeviceClassLibrary> og så blive præsenteret for alle metoderne. Men jeg har også brug for at kunne oprette flere instanser af Komando-klassen, så jeg kan have en device der kommunikerer med LAN Samtidig med en anden på COM... MEN jeg vil også meget gerne have punktumnotationen eks:

New Device1 (kommunikerer eks via COM)
New Device2 (kommunikerer eks via LAN)

Device1.Interface.COM.SetBaudrate(9600);
Device1.Interface.COM.SetComName("COM1")

Device2.Interface.LAN….osv


Device1.Commands.Get.Identifikation();
                                .Get.NogetAndet
Device1.Commands.Set.FilterValue(5);
                                .Set.NogetHeltTredje

eller

Device2.Commands.Get.Identifikation();
Device2.Commands. Set.FilterValue(5);


Når jeg så ”abonnerer” på Device1 eller Device2’s delegate kan jeg nemt se fra hvilken device resultaterne kommer



Kan ovenstående lade sig gøre på en eller anden måde???

Kan man oprette et object/instans som kan tilgås på ovenstående måde?



Mit ønske er at gøre DeviceClassLibrary.dll’en så overskuelig som overhoved mulig!

Og i øvrigt mange tak for jeres tålmodighed ;-)
Avatar billede aaberg Nybegynder
03. oktober 2008 - 08:46 #28
Så vidt jeg forstår dig nu, så vil du på en måde inddele metoder og properties i en klasse i kategorier, og få brugeren til at bruge punktumnotationen til at navigere disse kategorier. Dette kan ikke lade sig gøre.

Hvis du vil have punktumnotationen:

Device1.Commands.Get.Identification

Så skal du have en property i Device klassen som hedder Commands. Denne property skal indeholde en instans af en klasse som har en Property der hedder Get, som indeholder en instans af en klasse med som har en property der hedder Identifikation.. osv.

Men det du egentlig prøver på, er at lave om på designet af programmeringssproget. Hvorfor ikke bare have en GetIdentification() metode på Device klassen? Tror du ikke at brugeren forstår at GetIdentification er en kommando? Hvis Ikke, så kald metoden Device1.CommandGetIdentification(). Punktumnotationen er i C# til at navigere i namespaces og klasser/properties/metoder, hvis du laver om på dette, bliver brugerne af klasserne totalt forvirret. Det vil i hvert fald ikke gøre det mere overskueligt.

Keep it simple!
Avatar billede aaberg Nybegynder
10. oktober 2008 - 14:07 #29
Og svaret. :-)
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