3 Stimmen

Rückgabe einer generischen Liste

Ich versuche, nach dem Laden von Werten aus einer Datei eine generische Liste zurückzugeben. Trotz vieler Spielereien mit Typmanipulationen kann ich es immer noch nicht zum Funktionieren bringen. Der Code steht unten; meine Fragen sind:

  1. Muss ich jeden Schlüsseltyp identifizieren, wie ich unten beginne, oder gibt es einen schnelleren Weg? Ich sehe, dass 'where T: ...' hier relevant sein könnte, aber ich würde gerne DateTime, int, string, double usw. zulassen, wenn möglich, und ich sehe nicht, wie das mit 'where' gemacht werden kann.
  2. Warum kann ich mein DateTime-Element nicht zur Liste hinzufügen, die datetime ist?
  3. Wenn ich versuche, den Typ (listType) zu erhalten, scheint dieser den Gültigkeitsbereich zu verlassen. Selbst wenn ich den Typ in der Zeile darüber, wo ich ihn verwende, deklariere, heißt es, dass ein solches Objekt nicht existiert.

Vielen Dank für Ihre Gedanken

public static List FileToGenericList(string FilePath, int ignoreFirstXLines = 0, bool stripQuotes = true)
{
    List output = new List();

    Type listType = output.GetType().GetGenericArguments()[0];

    try
    {
        using (StreamReader stream = new StreamReader(File.Open(FilePath, FileMode.Open)))
        {
            string line;
            int currentLine = 0;

            while ((line = stream.ReadLine()) != null)
            {
                // Erste x Zeilen überspringen
                if (currentLine < ignoreFirstXLines) continue;

                // Anführungszeichen entfernen, falls erforderlich
                if (stripQuotes == true)
                {
                    line = line.Replace(@"""", @"");
                }

                // Q1 - MUSS ICH DAS FÜR JEDE ART HABEN ODER GIBT ES EINEN SCHNELLEREN WEG
                if (listType == typeof(System.DateTime))
                {
                    DateTime val = new System.DateTime();
                    val = DateTime.Parse(line);

                    // Q2 FEHLER: 'Argumenttyp ist nicht zu Typ 'T'' zuweisbar
                    output.Add(val);

                    // Aus irgendeinem Grund ist der Typ 'listType' von oben jetzt außerhalb des Gültigkeitsbereichs, wenn ich einen Cast versuche
                    output.Add((listType)val);
                }
                if (listType == typeof(System.String))
                {
                    //DateTime val = new System.DateTime();
                    //val = DateTime.Parse(line);
                    //output.Add(val.ToString());
                }

                // Fortfahren, um Zeilen für das Überspringen weiter zu verfolgen
                currentLine++;
            }
        }
    }
    catch (Exception ex)
    {
        throw new Exception("Fehler - beim Lesen der Datei unter " + FilePath + ist ein Problem aufgetreten. Fehlerdetails: " + ex.Message);
    }    
    return output;
}

3voto

Mike Chamberlain Punkte 34722

Statt Ihre Analyselogik in Ihre FileToGenericList-Methode zu codieren, denke ich, dass ein sauberer und flexiblerer Ansatz darin bestehen würde, dies auszulagern und als Lambda zu übergeben. Hier ist eine schnelle Konsolen-App, die diesen Ansatz demonstriert:

class Program
{
    static void Main(string[] args)
    {
        // Das zweite Argument ist ein Lambda, das beschreibt, wie die Zeile in den benötigten Typ konvertiert werden soll
        var dateList = FileToGenericList("dates.txt", DateTime.Parse);
        var stringList = FileToGenericList("strings.txt", s => s);
        var intList = FileToGenericList("integers.txt", Int32.Parse); 

        Console.ReadLine();
    }

    static List FileToGenericList(string filePath, Func parseFunc, int ignoreFirstXLines = 0, bool stripQuotes = true)
    {
        var output = new List();

        try
        {
            using (StreamReader stream = new StreamReader(File.Open(filePath, FileMode.Open)))
            {
                string line;
                int currentLine = 0;

                while ((line = stream.ReadLine()) != null)
                {
                    // Erste x Zeilen überspringen
                    if (currentLine < ignoreFirstXLines)
                        continue;

                    // Anführungszeichen entfernen, falls erforderlich
                    if (stripQuotes == true)
                        line = line.Replace(@"""", @"");

                    var parsedValue = parseFunc(line);
                    output.Add(parsedValue);
                    currentLine++;
                }
            }
        }
        catch (Exception ex)
        {
            throw new Exception("Fehler - Beim Lesen der Datei unter " + FilePath + " ist ein Problem aufgetreten. Fehlerdetails: " + ex.Message);
        }    
        return output;
   }
}

2voto

Eben Roux Punkte 12662

// Q1 - MUSS ICH DAS FÜR JEDEN TYP HABEN ODER GIBT ES EINEN SCHNELLEREN WEG

Hier ist etwas Testcode, um Ihnen den Einstieg zu erleichtern:

using System;
using System.Collections.Generic;

namespace AddGenericToList
{
    class Program
    {
        static void Main(string[] args)
        {
            var tc = new ListClass();

            tc.Add("a value");
            tc.Add(123);
            tc.Add(DateTime.Now);
        }
    }

    internal class ListClass
    {
        private readonly List list = new List();

        public void Add(object value)
        {
            list.Add((T)Convert.ChangeType(value, Nullable.GetUnderlyingType(typeof (T)) ?? typeof (T)));
        }
    }
}

Allerdings werden ungültige Umwandlungen einen Fehler auslösen. Zum Beispiel kann DateTime in string, aber nicht in int umgewandelt werden.

0voto

Avner Shahar-Kashtan Punkte 14250

Für Ihre Frage #3: Der Grund, warum Sie einen "out of scope" Fehler erhalten, ist, dass Sie nicht auf eine Variable umwandeln können. Ihr output.Add((listType)val); ist keine legale C# Anweisung - Sie können nur auf eine explizite Typdefinition umwandeln. Glücklicherweise müssen Sie nicht alle Ihre Umwandlungen über die Type listType Variable durchführen, da Sie eine explizite Typdefinition haben - das T, das Sie als generischen Parameter erhalten haben. Sie können die Antwort tief in der Antwort von @Pravin Pawar sehen: output.Add(val as T); oder noch besser die explizite Umwandlungssyntax verwenden output.Add((T)val), da T nicht unbedingt ein Referenztyp ist.

EDIT:

Sie haben recht, dass (T)val nicht kompiliert, da der Compiler nicht den zusätzlichen Aufwand für uns betreibt und entscheidet, dass T DateTime ist, trotz der Überprüfung, die wir zuvor hatten. Also können Sie dies tun:

 (T)Convert.ChangeType(val, typeof(T)));

Was Ihren DateTime val in T (der auch DateTime ist) umwandeln wird, was den Compiler ausreichend zufriedenstellen wird.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X