Avatar billede AOlsen Juniormester
29. juli 2016 - 16:03 Der er 12 kommentarer og
1 løsning

Generiske klasser og casting

Hej Eksperter

Jeg forsøger at læse og parse data fra forskellige filtyper. Logikken er, at jeg vil kunne læse data fra et antal forskellige filtyper og uanset filtypen returnere data i samme type objekt.

Derfor har jeg lavet en abstract klasse med to abstrakte metoder og 1 public, der tillader at læse flere filer på 1 gang.
public abstract class AbstractImportFactory<T>
  protected abstract T ReadFile(string fileName);
  protected abstract IEnumerable<MyDataType> Parse(T model);
  public List<MyDataType> ReadFiles(string[] filePaths)
    {
            List<MyDataType> list = new List<MyDataType>();
            foreach (string filePath in filePaths)
            {
                T model = ReadFile(filePath);
                foreach(MyDataType mdt in Parse(model);
                        list.Add(mdt)     
            }
            return list;
  }

Så har jeg lavet 1 klasse for hver filtype jeg gerne vil understøtte a la
public class FileType1ImportFactory :  AbstractImportFactory <FileType1DataModel>

Bemærk at jeg har udspecificeret det generiske T i klassen til den relevante lokale datamodel klasse.

Jeg løber ind i problemer, når jeg forsøger at implementere et factory pattern i hovedklassen.

public abstract class AbstractImportFactory<T>
{
    ....
  public static AbstractImportFactory<T> GetFactory(ImportFormat iFormat, BackgroundWorker worker)
{
            switch (iFormat)
            {
                case ImportFormat.NotSpecified:
                    break;
                case ImportFormat.FileType1Format:
                    return new FileType1ImportFactory (worker)    <- Fejl
                default:
                    throw new ArgumentOutOfRangeException("iFormat");
      }       
}

FileType1ImportFactory er som jeg ser det en AbstractImportFactory<T>, hvor T er sat til "FileType1DataModel".

Hvordan caster jeg FileType1ImportFactory til AbstractImportFactory<T>?
Avatar billede arne_v Ekspert
29. juli 2016 - 16:21 #1
Det er nemt at forklare hvorfor du faar fejlen. Koden er ikke type safe.

Det er straks svaerere at komme med en god loesning paa problemet.
Avatar billede arne_v Ekspert
29. juli 2016 - 16:36 #2
Det er absolut helle ikke godt at basis klassen skal have kendskab til sub klasser.

Men igen hvad saa.
Avatar billede arne_v Ekspert
29. juli 2016 - 16:55 #3
Det her compiler:


using System.Collections.Generic;

namespace E
{
    public abstract class DataModel
    {
    }
    public class FileType1DataModel : DataModel
    {
    }
    public abstract class AbstractImportFactory<T> where T : DataModel
    {
        private static  Dictionary<string, AbstractImportFactory<T>> repo = new Dictionary<string, AbstractImportFactory<T>>();
        public static AbstractImportFactory<T> GetFactory(string impl)
        {
            return repo[impl];
        }
        public static void RegisterImportFactory(string impl, AbstractImportFactory<T> factory)
        {
            repo.Add(impl, factory);
        }
        public abstract T GetOne();
    }
    public class FileType1ImportFactory :  AbstractImportFactory <FileType1DataModel>
    {
        public override FileType1DataModel GetOne()
        {
            return new FileType1DataModel();
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            AbstractImportFactory<FileType1DataModel>.RegisterImportFactory("Type1", new FileType1ImportFactory());
            AbstractImportFactory<FileType1DataModel> f = AbstractImportFactory<FileType1DataModel>.GetFactory("Type1");
            FileType1DataModel dm = f.GetOne();
            Console.ReadKey();
        }
    }
}
Avatar billede arne_v Ekspert
29. juli 2016 - 16:56 #4
Men om det matcher dine behov er en anden sag.
Avatar billede softspot Forsker
29. juli 2016 - 17:04 #5
Jeg forstår ikke helt, hvorfor du vil have flettet factoryklassen og formatlæseren sammen i den samme klasse. Det er vel to forskellige opgaver de har og burde derfor splittes i to separate "klassehierarkier".

Jeg har lavet en lign. løsning, hvor jeg har anvendt en factory til at skabe reader-klasserne og så et interface til readerklasserne. Jeg har så anvendt Reflection til at oprettet readerklasserne, men Arnes metode er givetvis hurtigere, hvis man skal skabe mange klasser via factoryklassen.
Avatar billede arne_v Ekspert
29. juli 2016 - 17:17 #6
Avatar billede AOlsen Juniormester
29. juli 2016 - 21:24 #7
@Softspot: Factory delen kan vel ligge i hvilken som helst klasse med samme resultat, eller? Det er jvf Arne ikke typesafe at returnere AbstractImportFactory<T> fra en metode. Jeg kan ikke se hvordan jeg kan skille dem ad uden at ramme samme udfordring?

Arne_v: Det er over min forstand, at C# tillader type skiftet, når det er input til en metode. Vores tilgange er jo identiske, bortset fra, at jeg vil returnere FileType1ImportFactory som en AbstractImportFactory<T>, mens du sender FileType1ImportFactory ind som AbstractImportFactory<T> parameter i RegisterImportFactory metoden.

Det giver ikke mening for mig .-) Er det reglen at man kan sende ind men ikke returnere?
Avatar billede softspot Forsker
29. juli 2016 - 23:10 #8
Det har du ret i og jeg tror egentlig også min undren var fordi jeg ikke helt havde gennemskuet hvad du forsøgte.

Jeg spekulerer lidt på, hvilken fejl det er du rent faktisk får? Det synes jeg ikke lige jeg kan få øje på nogen steder i dit spørgsmål (beklager hvis jeg har overset det).
Avatar billede arne_v Ekspert
30. juli 2016 - 02:07 #9
Med sin kode kunne man kalde  AbstractImportFactory<X> .GetFactory() og faa en Y returneret uden fejl.

Det kan man ikke med min kode.
Avatar billede arne_v Ekspert
30. juli 2016 - 02:07 #10
Men jeg tror faktisk slet ikke at Dictionary er noedvendigt hvis du kun har en factory til hver type.
Avatar billede arne_v Ekspert
30. juli 2016 - 15:45 #11
Hvis du vil fortsatte som du er startet, saa compiler foelgende:

return (AbstractImportFactory<T>)(object)(new FileType1ImportFactory(worker));

men det er ikke paent!
Avatar billede AOlsen Juniormester
01. august 2016 - 13:05 #12
Arne_V: Angående hvilken fejl jeg får, så kunne jeg ikke kompilere fejl linien, da casting mellem Abstract klassen og concrete klassen ikke er muligt.

Jeg har markeret dit svar, hvor casting går via object som svar på min udfordring. Det compiler og gør at jeg kan fortsætte med mit oprindelige design.

Tak for hjælpen
Avatar billede arne_v Ekspert
01. august 2016 - 14:32 #13
OK. Bare forstaa at der er visse ulemper ved det design.
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