Avatar billede Gyvelvej10 Nybegynder
16. oktober 2011 - 21:07 Der er 13 kommentarer og
1 løsning

Activator.Createinstance

Jeg har fået problemer med en metode til at oprette objekter af en klasse i runtime.
Jeg har hentet en liste over de klasser der er i namespacet, og vil nu oprette et objekt af hver klasse.

Jeg fik dog et problem når jeg begyndte at bruge disse objekter.
"cap" som er oprette "normalt" virker i min senere kode,
men det gør "act" som er oprettet med activator ikke.

Jeg så gerne, at jeg fik den samme type object ud af understående kode, når jeg beder om af få skrevet typen af objekterne ud, får jeg også samme resultat.
Men jeg faldt over følgende i min watch liste.

Name    Value                  Type
cap    {GLV.Logfiles.Captain}    GLV.Logfiles.Captain
act    {GLV.Logfiles.Captain}    object {GLV.Logfiles.Captain}


Nogle der kan forklare mig hvad jeg gør galt, eller hvorfor jeg ikke får helt ens typer.

Jeg har brugt en søndag på dette problem nu, og håber herfor fra en livlinie fra jer.

Venlig hilsen
Jørn


Kode
-------------------------------------------
object act;
Logfiles.Captain cap = new Captain();
System.Type ttt = typeof(Logfiles.Captain);
                             
act = (Logfiles.Captain)Activator.CreateInstance(typeof(Logfiles.Captain));

testview.Appendtext(act.ToString());
testview.Appendtext(act.GetType().ToString());
                                testview.Appendtext(cap.ToString());
testview.Appendtext(cap.GetType().ToString());
-------------------------------------------
Avatar billede arne_v Ekspert
16. oktober 2011 - 21:16 #1
Der sker et eller andet mystisk i din kode.

using System;

namespace GLV.Logfiles
{
    public class Captain
    {
       
    }
}

namespace E
{
    using GLV.Logfiles;
    public class Program
    {
        public static void Main(string[] args)
        {
            Captain cap = new Captain();
            object act = (Captain)Activator.CreateInstance(typeof(Captain));
            Console.WriteLine(act.ToString());
            Console.WriteLine(act.GetType().ToString());
            Console.WriteLine(cap.ToString());
            Console.WriteLine(cap.GetType().ToString());
            Console.ReadKey();
        }
    }
}

udskriver paent:

GLV.Logfiles.Captain
GLV.Logfiles.Captain
GLV.Logfiles.Captain
GLV.Logfiles.Captain

Hvor kommer de { } fra ??
Avatar billede Gyvelvej10 Nybegynder
16. oktober 2011 - 21:38 #2
Hej Arne

Det kan jeg ikke finde ud af :-)

Fik fejl i min efterfølgende metoder, når jeg brugt de objekter der var lavet med activator, men ikke hvis jeg opretter dem "normalt"
Jeg begyndte at fejlsøge, men objekterne så helt ens ud.

Faldt dog over at typen ikke er ens i watch listen.
Kan jeg få dig til at se hvad din watch liste siger til de 2 objekter?

Jørn
Avatar billede arne_v Ekspert
16. oktober 2011 - 21:58 #3
Der er vist ingen grund til at checke det - jeg har rimelig tillid til at .GetType() returnerer det rigtige.

Generelt er det svaert at troubleshoote udfra "Fik fejl" og "typen ikke er ens".

Er du sikker paa at det ikke er fordi at du ved new bruger constructor med argumenter og den info mangler ved reflection?
Avatar billede Gyvelvej10 Nybegynder
16. oktober 2011 - 23:03 #4
Jeg har prøvet at steppe mig igennem programmet trin for trin.
De bruger samme construktor (paremeterless)
Jeg har bemærket at activator kun vil bruge parameterless construktor.


Har lige prøvet at lave en compare på de 2 objekttyper,
og her er resultatet at de er ens.
Jeg forstår ikke hvorfor at jeg kun får problemer med den der oprettes med activator.

Her er en kopi af klassen jeg opretter, samt metoden der fejler med activator objektet.

Håber dine friske øjne kan se brøleren.

Venlig hilsen
Jørn

Captain klassen.
-------------------
namespace GLV.Logfiles
{
    [XmlRoot(ElementName = "log", IsNullable = false)]
    public class Captain :Logfile
    {
        public Captain()
        {
        }

        [XmlElement("entry")]
        public List<Entry.CaptainEntry> EntryList
        {
            get;
            set;
        }
    }
}
---------------------------
Den fejler i denne metode, ved Deserialize.
{"Der er en fejl i XML-dokumentet (2, 2)."}
innerexception:{"<log xmlns=''> var ikke forventet."}
Det er samme xml fil jeg bruger, og jeg har ikke arbejdet med objekterne mellem construktion og denne metode.
--------------------------------

private T ImportLogfile<T> (FileInfo fi,T obj)
{
           
            //TODO try catch

XmlSerializer deserializer = new XmlSerializer(typeof(T));
TextReader textReader = new StreamReader(fi.FullName);
obj = (T)deserializer.Deserialize(textReader);
textReader.Close();
return obj;
}
Avatar billede arne_v Ekspert
16. oktober 2011 - 23:14 #5
Den kode bruger slet ikke obj argumentet, saa problemt kan ikke vaere med hvad der er i det.

Fejlen beskeden antyder ogsaa ret kraftigt at fejln relaterer sig til indholdet af XML filen.
Avatar billede Gyvelvej10 Nybegynder
16. oktober 2011 - 23:53 #6
OK, vil kigge på det igen imorgen med frisk øjne.

Tak for dine input.

Jørn
Avatar billede johny Nybegynder
17. oktober 2011 - 10:05 #7
Hej Jørn

Det er udelukkende fordi du gemmer det i en object variabel. Det debuggeren fortæller dig er, at din objekt reference er at typen "object", men det den peger på er i virkeligheden en "GLV.Logfiles.Captain".

Det har altså ikke noget at gøre med din brug af reflektion at du får et anderledes resultat, men udelukkende din brug af variabler.

Mvh Johny
Avatar billede Gyvelvej10 Nybegynder
17. oktober 2011 - 22:51 #8
Så har jeg kigget lidt på det igen.

Jeg har prøvet med en anden xml fil, som er generet af et andet system. Det er samme problem.
Kører når jeg opretter object "normalt", men ikke med activator.

Johny, jeg lavede lige dette forsøg.

/// Denne kode giver fejl i ImportLogfile
System.Type st = kvp.Key; // get type from dictionary
object obj; // temp object
obj = Activator.CreateInstance(st); //make object of st type.
ImportLogfile(fi, obj);

///Denne kode kører uden problemer i ImportLogfile
System.Type st = kvp.Key; // get type from dictionary
Captain obj; // temp object
obj = (Captain)Activator.CreateInstance(st); //make object of st type.
obj = ImportLogfile(fi, obj);

Men med den nedstående kode, skal jeg kende typen inden compiletime, og ikke som jeg ønsker det i runtime.

Skal jeg kende typen i compiletime, når jeg bruger activator.createinstance(type) ?

Hvordan laver i et object i runtime, ud fra system.type ?

Johny, jeg er tilfreds med dit svar.
men venter lige med at lukke spørgsmålet til imorgen.
Jeg er ikke hardcore bruger herinde, og er bange for at i ikke ville kunne svare, hvis jeg lukker spørgsmålet.
Avatar billede Syska Mester
18. oktober 2011 - 09:26 #9
Har du overvejet at bruge "dynamic" ?
Avatar billede johny Nybegynder
18. oktober 2011 - 14:07 #10
@jørn: Hvilket problem er det du prøver at løse? Min pointe er, at det watch listen fortæller dig, blot er at du har gemt den ene instans i en objekt variabel og den anden instans i en typestærk variabel. Med andre ord: al din kode virker, watchlisten gør dig blot opmærksom på denne forskel.

Så nej, du skal ikke bruge typen hvor du instantierer objektet. :)
Avatar billede Gyvelvej10 Nybegynder
18. oktober 2011 - 16:33 #11
Jeg har en del xmlfiler jeg vil have deserialized.
I den nuværende kode har jeg en del (formange) if sætninger.

if(file.name == "Captain")
{
Captian cap = new Captian;
ImportLogFile(File,Cap); //sender fil og objektet til metoden
}
if(xxxxxxx)
{
Næste xml type
}
osv...


Jeg vil gerne gøre det pænere, samt undgå at skulle rette i koden når jeg skulle tilføje en ny xml type.

Jeg har lavet følgende for at gøre det mere dynamisk:

- Oprettet alle xml klasser under samme namespace.
- Henter System.Type på dette namespace(datastruktur over klasser som afspejler xml typer)
- Brugeren kan nu vælge hvilken xml typer der skal importeres.
- Jeg gennemgår nu en filmappe med diverse xml filer.
- Hvis xml filen er i datastrukturen over xmltyper og brugeren har valgt at importer denne type, sender jeg xml filen og et objekt der afspejler denne type til nedstående metode.

Hvis jeg vil tilføje en ny xml type, skal jeg oprette en ny klasse som afspejler xml filen, men ikke rette i andre klasser.

Jeg kan godt se problemet nu.
Metoden deserialized xml filen til det medsendte objekt.
Når det er en strongtype er der ingen problem.
Men når jeg laver det med en objekt variable, vil metoden prøve at deserialized min xml fil til typen objekt, og jeg får fejlen om at der er problemer med xml filen.

--------------------------------
private T ImportLogfile<T> (FileInfo fi,T obj)
{
XmlSerializer deserializer = new XmlSerializer(typeof(T));
TextReader textReader = new StreamReader(fi.FullName);
obj = (T)deserializer.Deserialize(textReader);
textReader.Close();
return obj;
}
-------------------------------

Jeg har prøvet at sende system.typen med til metoden.
Nu får jeg deserialized xml filen korrekt.

-------------------------------
private T ImportLogfile<T> (FileInfo fi,T obj ,System.Type st)
{
XmlSerializer deserializer = new XmlSerializer(st);
TextReader textReader = new StreamReader(fi.FullName);
obj = (T)deserializer.Deserialize(textReader);
textReader.Close();
return obj;
}
--------------------------
Men jeg får stadig en objekt variable tilbage ikke et objekt som afspejler xmltypen.ØV.
Kan jeg ikke lave objekttypen om med systemtypen, eller noget lign. ?

obj.ConvertTo(system.type);


Takker for jeres input.
Må indrømme at jeg ikke er så stærk på dette område.
Første gang jeg prøver at lave noget dynamisk.
Jørn
Avatar billede johny Nybegynder
18. oktober 2011 - 20:22 #12
Det virker lidt som om at du blander dynamisk og statisk kode sammen her. Hvad vil du præcis opnå? Hvis det f.eks. skal være typestærkt det du koder op imod, hvorfor vil du så til at lave dynamisk kode, og omvendt, hvis du gerne vil have dynamisk kode, hvad skal du så med andet en objekter?

Der findes kun to måder at gå imellem de to verdener, som egentlig lidt er det samme:
1) Enten kan du lade alle klasserne have den samme basistype, hvor du kan ligge forskellige ting på, som du så kan bruge på tværs af det hele,
2) eller du kan lave dine if sætninger og så typecaste det hele igen når du har deserialiseret dine xml objekter.

Det der dog undrer mig mest, er det at du har brug for at køre med andet end objekter, i hvilken sammenhæng skal du bruge det, hvis der virkelig er tale om dynamisk kode?
Avatar billede johny Nybegynder
18. oktober 2011 - 20:26 #13
Alternativt kan du også prøve at sende mig din kode samt nogle eksempelfiler. Det kan jo også være vi taler forbi hinanden.
Avatar billede Gyvelvej10 Nybegynder
18. oktober 2011 - 21:10 #14
Hej Johny

Som sagt er det ret nyt for mig.
Jeg har allerede en struktur, hvor alle logfilerne har en baseclass.

Løsningen var at ændre min struktur fra at gemme som strongtype, til denne baseclass.

Jeg skal lige vende mig til tankegangen.
Prøver at arbejde videre med at gøre min datastruktur dynamisk.

Går det helt galt skal jeg nok give lyd fra mig.
Ellers mange tak for sparket videre :-)

Jørn
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