Avatar billede friiiiis Novice
12. maj 2015 - 14:57 Der er 10 kommentarer og
1 løsning

List<...> og Interfaces

Hej,

Jeg har opbygget en række lister baseret på følgende herunder:

Mit spørgsmål er nu:

Jeg får nogle data defineret som:

List<IKKSDataPoint> Data = "en eller anden funktion"

Hvorfor kan jeg ikke caste dem som

List<KKSDataPoint2> data = (KKSDataPoint2)Data;// her får jeg fejl
List<KKSDataPoint> data = (KKSDataPoint)Data;// her får jeg fejl

Jeg får fejlen "cannot implicitely convert from List<IKKSDataPoint> to List<KKSDataPoint2>"?? Hvad skal jeg skrive for at det til at lykkes??



public interface IKKSDataPoint
    {
        DateTime dateAndTime { get; set; }
        double value { get; set; }
        string Unit { get; set; }
        string Description { get; set; }
        string KKS_Key { get; set; }

        string ToString();
    }

    public class KKSDataPoint2 : BaseKKSDataPoint, IKKSDataPoint
    {
        public override string ToString()
        {
            return this.KKS_Key;
        }
    }

    public class KKSDataPoint : BaseKKSDataPoint, IKKSDataPoint
    {
        public override string ToString()
        {
            return this.Description + " (" + this.KKS_Key + ") ";
        }
    }

    public abstract class BaseKKSDataPoint
    {
        public DateTime dateAndTime { get; set; }
        public double value { get; set; }
        public string Unit { get; set; }
        public string Description { get; set; }
        public string KKS_Key { get; set; }
    }
Avatar billede friiiiis Novice
12. maj 2015 - 15:05 #1
Tja, der var jå et riemliogt godt svar på stackoverflow: http://stackoverflow.com/questions/6019765/how-do-i-convert-a-listinterface-to-listconcrete

men ville nu gerne have jeres kommentarer alligevel...
Avatar billede softspot Forsker
12. maj 2015 - 15:22 #2
Det er vel fordi du forsøger at caste fra et enkelt objekt til en liste af det objekt

List<KKSDataPoint2> data = (KKSDataPoint2)Data;// her får jeg fejl

Du skal vel caste således (forudsat Data rent faktisk er en liste):

List<KKSDataPoint2> data = (List<KKSDataPoint2>)Data;
Avatar billede softspot Forsker
12. maj 2015 - 15:51 #3
Nå, efter at have læst SO-indlægget, forstår jeg, at problemet ikke er det jeg opfattede ud fra det kode du har vist, men snarere, at der er en potentiel inkompatibilitet mellem det du forsøger at caste til og det du forsøger af caste fra.

SO-indlægget siger vel, hvad der skal siges om det... eller hvad?
Avatar billede sonalias Seniormester
12. maj 2015 - 16:23 #4
"List<KKSDataPoint2> data = (KKSDataPoint2)Data;// her får jeg fejl"
Her forsøger du at oprette en ny liste af klassen "KKSDataPoint2", men du har kun defineret en "KKSDataPoint" klasse.

Prøv istedet :
List<KKSDataPoint> data1 = new List<KKSDataPoint>;
List<KKSDataPoint> data2 = new List<KKSDataPoint>;

Nu kan du så tilføje til dine lister som begge er af klassen KKSDataPoint.
data1.add()
data1[0].value = 2;
data1[0].dateAndTime = DateTime.Now;

osv osv
Avatar billede friiiiis Novice
12. maj 2015 - 21:44 #5
Hej,

Jeg har ikke fået spørgsmålet defineret korrekt( som du egentlig også skriver softspot)

Jeg har 2 class ( KKSDataPooint og KKSDataPoint2) med samme fields men med forskellig definition af metoden ToString.

Jeg opretter en liste med KKSDataPoint List<KKSDataPoint> og vil nu gerne caste den til List<KKSDataPoint2> fordi jeg har brug for dens definition af ToString metoden. For at gøre det har jeg lavet begge class arve fra en interface IKKSDataPoint men som beskrevet herover virker det ikke...

Til 100 point vil jeg tillade mig at grave videre: hvordan løser man så problemet?? Hvordan vil I profesionelle programmører løse det hvis i har een liste og men har brug for 2 definitioner af ToString metoden fra denne liste af objekter...???
Avatar billede softspot Forsker
12. maj 2015 - 22:15 #6
Spørgsmålet er, hvorfor du overhovedet har brug for at konvertere listen til en specifik class?

Dit interface giver adgang til de egenskaber der tilbydes (ToString er også defineret i dit interface), så du kan jo bare kalde ToString på elementerne i listen.

Du burde måske definere ToString i din base-class (som abstract) og i det hele taget lade din base-class implementere interfacet (hvis ellers man kan det på en abstract class)...


public abstract class BaseKKSDataPoint : IKKSDataPoint
{
  ...
  public abstract string ToString();
}

public class KKSDataPoint : BaseKKSDataPoint
{
  ...
  public override string ToString() { ... }
}

public class KKSDataPoint2 : BaseKKSDataPoint
{
  ...
  public override string ToString() { ... }
}


Når du så skal gennemløbe din liste og have en strengrepræsentation af data i listen, kan du bare anvende listen af interface uden at skulle konvertere til en specifik klasse:

foreach(var item in Data)
{
  Console.WriteLine(item.ToString());
}


Her antager jeg, at Data er af typen List<IKKSDataPoint>.
Avatar billede friiiiis Novice
13. maj 2015 - 15:16 #7
Ikke forstået softspot:

Helt generelt: jeg har en liste med data og i nogle tilfælde har jeg brug for een version af ToString metoden og i andre tilfælde har jeg brug for en anden version af ToString metoden...

Det tilfælde du kommer med forklarer ikke hvordan jeg får den ene ift. den anden version af ToString ? Jeg mente at jeg kunne caste fra den een lise af classses til den anden liste af classes hvad jeg så ikke kan...

- og så har du i øvrigt ret - jeg bør definerer ToString som en abstract metode i Base Class
Avatar billede softspot Forsker
13. maj 2015 - 16:24 #8
Det du arbejder med her er polymorfisme, hvor du ikke er så interesseret i det enkelte objekte konkrete type, men snarere de egenskaber det har. Disse egenskaber udstilles i dit tilfælde igennem et interface (og i din base-klasse).

Du bør slet ikke have behov for at caste, da klienten der bruger listen blot skal kalde ToString på det aktuelle objekt (via interfacet). Det aktuelle objekt sørger for, at det er den rigtige implementering der kaldes (kva den override du har lavet af ToString på det pågældende objekt).

Så jeg tænker du er nød til at uddybe, hvorfor du mener du har behov for, at caste objekterne i din liste. Det lyder lidt som om, du ikke helt har set lyset i polymorfismen...
Avatar billede softspot Forsker
13. maj 2015 - 16:40 #9
Med udgangspunkt i de klassedefinitioner jeg lavede i #6, burde eksemplet måske have involveret en initialisering af listen med data, for at gøre det klarere, hvordan det bør bruges. Derfor viser jeg lige et udvidet eksempel på dette:

var Data = new List<IKKSDataPoint>();
Data.Add(new KKSDataPoint { KKS_Key = "DataPoint", Description = "Testing" });
Data.Add(new KKSDataPoint2 { KKS_Key = "DataPoint2" });

foreach(var item in Data)
{
  Console.WriteLine(item.ToString());
}

Data (listen) kan indeholde objekter der implementerer interfacet IKKSDataPoint og løkken er ligeglad med hvilken konkret type objektet (item) har, da det implementerer IKKSDataPoint og dermed ToString.

Kaldet til ToString aktiverer den implementering som findes i klassen som det konkrete objekt, item, er en type af.

Det samme ville have gjort sig gældende, hvis du havde erklæret listen af typen BaseKKSDataPoint:

var Data = new List<BaseKKSDataPoint>();
Data.Add(new KKSDataPoint { KKS_Key = "DataPoint", Description = "Testing" });
Data.Add(new KKSDataPoint2) { KKS_Key = "DataPoint2" });

foreach(var item in Data)
{
  Console.WriteLine(item.ToString());
}

Så i dit tilfælde behøver du faktisk ikke interfacet.

Prøv eksemplerne og se det i funktion (disclaimer: jeg har ikke testet det).
Avatar billede friiiiis Novice
17. maj 2015 - 10:57 #10
Hej Softspot,

Ja, du kæmper sgu godt for sagen - Tak for det ;-) Smid et svar ;-)

jeg forstår godt ovenstående men problemet er at jeg en Liste bestående af KKSDataPoint men jeg har brug for ToString metoden  i klassen KKSDataPoint2.

Jeg kan ikke ændre på at uanset hvad så får jeg data som Liste<KKSDataPoint>. Nogle gange skal jeg bruge ToString metoden i KKSDataPoint og andre gange ToString metoden i KKSDatPoint2.

Altså - uanset at objektet er KKSDataPoint så har jeg brug for at bruge metoden Tostring i KKSDataPoint2 - derfor vil jeg gerne caste fra KKSDataPoint til KKSDataPoint2 med brug af Interfacet IKKSDataPoint

ANYWAY - jeg løste problemet på følgende måde (ikke smukt men det virker...). Hvis du har et bedre forslag så sig til:


            List<KKSDataPoint> Data = ... Data generator...;
            List<KKSDataPoint2> Data2 = new List<KKSDataPoint2>();
            foreach (KKSDataPoint kks in Data)
            {
                KKSDataPoint2 kks2 = new KKSDataPoint2();
                kks2.dateAndTime = kks.dateAndTime;
                kks2.Description  = kks.Description;
                kks2.KKS_Key  = kks.KKS_Key;
                kks2.Unit  = kks.Unit;
                kks2.value  = kks.value;
                Data2.Add(kks2);
            }
Avatar billede softspot Forsker
17. maj 2015 - 12:02 #11
Ja, det kan du jo ikke. Det er forkert brug af arv. Du kan i stedet bruge en serviceklasse. Serviceklassen implementerer så et interface, der tillader dig at implementere forskellig formatering af KKSDataPoint.

På KKSDataPoint implementerer du en variant af ToString, der tager en parameter af interfacet IKKSDPFormat, som parameter:

public interface IKKSDPFormat
{
    string AsString(KKSDataPoint dp);
}

Et par konkrete implementeringer kunne se således ud:

public class KKSDPFormat : IKKSDPFormat
{
    public string AsString(KKSDataPoint dp)
    {
        return dp.KKS_Key;
    }
}

public class KKSDP2Format : IKKSDPFormat
{
    public string AsString(KKSDataPoint dp)
    {
        return dp.Description + "(" + dp.KKS_Key + ")";
    }
}

Du har så faktisk ikke brug for dit IKKSDataPoint-interface, men kan i stedet implementere din klasse således:

public class KKSDataPoint
{
    public DateTime dateAndTime { get; set; }
    public double value { get; set; }
    public string Unit { get; set; }
    public string Description { get; set; }
    public string KKS_Key { get; set; }

    public string ToString(IKKSDPFormat formatter)
    {
        return formatter.AsString(this);
    }
}

Når du så skal have udskrevet dine objekter som strenge, kan du kalde din implementering af ToString, der tager et Formateringsobjekt således:

List<KKSDataPoint> Data = ... Data generator...;
foreach (var kks in Data)
{
    // udskriv nøglen alene
    kks.ToString(new KKSDPFormat());

    // udskriv nøglen i parentes foranstillet beskrivelsen
    kks.ToString(new KKSDP2Format());
}

Din udfordring er, at du gerne vil repræsentere et objekts data forskelligt, afhængig af nogle eksterne omstændigheder og derfor kan objektet ikke selv tage beslutningen (fordi det helst ikke skal kende til sin kontekst). Den beslutning må du overlade til en anden del af systemet, hvor det giver mere mening, f.eks. den kontekst, hvor strengrepræsentationen skal bruges (som illustreret ovenfor).
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

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