Odczytanie pola klasy po nazwie

0

Witam,
mam listę obiektów klasy Foo:

public class Foo
{
    public string name1;
    public string name2;
    public string mail;
}

List<Foo> records;

foreach (var rec in records)
{
    var fieldValues = rec.GetType().GetFields().Select(field => field.GetValue(rec)).ToList();
}

W pętli pobieram wszystkie wartości jednego rekordu danych.
Pytanie jak pobrać wartość konkretnej zmiennej, np. "name2"?

Pozdrawiam.

4

A czemu chcesz się odnieść do pola po nazwie? Nie lepiej zrobić mapę z kluczami string?

1

name2 nie jest zmienną a polem -> uzyj Mapy zwanej w C# Dictionary

0
var fieldValues = records.Select(field => field.name2).ToList();
0
_13th_Dragon napisał(a):
var fieldValues = records.Select(field => field.name2).ToList();

A co w przypadku kiedy zamiast:

List<Foo> records;

Mam typ generyczny:

List<T> records

Wtedy to rozwiązanie nie zadziała.

1
Janko M. napisał(a):
_13th_Dragon napisał(a):
var fieldValues = records.Select(field => field.name2).ToList();

A co w przypadku kiedy zamiast:

List<Foo> records;

Mam typ generyczny:

List<T> records

Wtedy to rozwiązanie nie zadziała.

Może opisz dokładniej co próbujesz zrobić? Większy big picture.

1
Janko M. napisał(a):

Mam typ generyczny:

List<T> records

Wtedy to rozwiązanie nie zadziała.

            foreach (var rec in records)
            {
                var fields=rec
                    .GetType()
                    .GetFields()
                    .Where(field => field.IsPublic)
                    .Select(field => new { name = field.Name, value = field.GetValue(rec).ToString() })
                    .ToList()
                ;
                foreach(var value in fields) Console.WriteLine(value.name+": "+value.value);
                Console.WriteLine();
            }

Z tym że nie tędy droga, obstawiałbym że nie wiesz co czynisz.

0

Mam tak:

List<Foo> testRecords = new List<Foo>();
testRecords.Add(...);
testRecords.Add(...);

ReadData(testRecords.Cast<object>().ToList());

public void ReadData<T>(List<T> records)
{
	foreach (var rec in records)
	{
		var fieldValues = rec.GetType().GetFields().Select(field => field.GetValue(rec)).ToList();
	}
}

W metodzie ReadData() chcę odczytać wartość pola name2, to wszystko.

0
Janko M. napisał(a):

Mam tak:

List<Foo> testRecords = new List<Foo>();
testRecords.Add(...);
testRecords.Add(...);

ReadData(testRecords.Cast<object>().ToList());

public void ReadData<T>(List<T> records)
{
	foreach (var rec in records)
	{
		var fieldValues = rec.GetType().GetFields().Select(field => field.GetValue(rec)).ToList();
	}
}

W metodzie ReadData() chcę odczytać wartość pola name2, to wszystko.

A co masz w tych?

testRecords.Add(...);
testRecords.Add(...);

Bo tam inicjujesz swoje Foo, więc pewnie wkładasz do nich to co chcesz wyciągnąć z name2.

0
Riddle napisał(a):

Bo tam inicjujesz swoje Foo, więc pewnie wkładasz do nich to co chcesz wyciągnąć z name2.

Tak, ale muszę to zrobić w metodzie

ReadData()
1
Janko M. napisał(a):
Riddle napisał(a):

Bo tam inicjujesz swoje Foo, więc pewnie wkładasz do nich to co chcesz wyciągnąć z name2.

Tak, ale muszę to zrobić w metodzie

ReadData()

Czemu niby?

Jedyny powód żeby to zrobić, to gdyby ten Foo był klasą-workiem, żeby zdeserializować jakieś dane np z tabelki lub z JSON'a, i chcesz odczytać pola tej klasy, żeby iterować po kluczach tej deserializowanej struktury.

Jeśli nie (a wygląda na to że nie), to moim zdaniem to nie jest najlepszy pomysł żeby czytać klucze klasy. Pomyśl o konsekwencjach tego, zrobisz potem refaktor, zmienisz nazwę pola, i aplikacja zacznie inaczej działać - to nie jest dobry pomysł. Zmienisz nawet kolejność pól, i aplikacja może inaczej działać, albo zrefaktorujesz ich typ - znów ten sam problem.

1
Janko M. napisał(a):

A co w przypadku kiedy zamiast:

List<Foo> records;

Mam typ generyczny:

List<T> records
Janko M. napisał(a):

W metodzie ReadData() chcę odczytać wartość pola name2, to wszystko.

Może się jednak zdecydujesz, typ generyczny czy też pole o konkretnej nazwie ...

            foreach(var rec in records)
            {
                var fields=rec
                    .GetType()
                    .GetFields()
                    .Where(field => field.IsPublic)
                    .Where(field => field.Name=="name2")
                    .Select(field => field.GetValue(rec).ToString())
                    .ToList()
                ;
                foreach(var value in fields) Console.WriteLine(value);
                Console.WriteLine();
            }

Z tym że jak mówiłem wcześniej: - nie tędy droga.

1

Model niech implementuje interfejs lub rozszerza klasę abstrakcyjną, wtedy generyczna metoda narzuca constraint, że T : IYourModelWithName2FieldWhichIsByTheWaySooGreatNameForAField i nie potrzebujesz refleksji. Refleksja to zło, bo źle użyta bardzo utrudnia refactoring, zaciemnia logikę, a za to bardzo łatwo o ukryte błędy, które wyjdą dopiero przy uruchomieniu testów lub - co dość prawdopodobne - na produkcji.

0

Problem jest taki że do metody ReadData(), wchodzą obiekty różnych klas (stąd typ generyczny). Zadaniem metody ReadData() jest na podstawie fieldName zwrócić wartość pola w danej klasie. Podane rozwiązanie przez @_13th_Dragon jest OK, pytanie co zrobić kiedy pole w klasie jest kolekcją?

public void ReadData<T>(List<T> records, string fieldName)
{

}
1
Janko M. napisał(a):

Problem jest taki że do metody ReadData(), wchodzą obiekty różnych klas (stąd typ generyczny). Zadaniem metody ReadData() jest na podstawie fieldName zwrócić wartość pola w danej klasie.

To czemu nie zrobisz że te obiekty różnych klasa mają wspólny podtym (wspólny interfejs albo wspólna klasa bazowa), i nie wyciągniesz odpowiednich danych przez metodę lub metodę abstrakcyjną?

To byłoby dużo lepsze wyjście niż czytanie pól po nazwie.

0

Teraz mi jeszcze przyszło do głowy, że możesz użyć dynamic, wtedy nie potrzebujesz generyka, ani nawet refleksji, po prostu odwołujesz się do pola jak do słownika - dynamicObject[fieldName]. Zresztą, umówmy się, nie potrzebowałeś generyka od początku, mogłeś przyjmować jako argument object.

0
Riddle napisał(a):

To czemu nie zrobisz że te obiekty różnych klasa mają wspólny podtym (wspólny interfejs albo wspólna klasa bazowa), i nie wyciągniesz odpowiednich danych przez metodę lub metodę abstrakcyjną?
To byłoby dużo lepsze wyjście niż czytanie pól po nazwie.

Problem sprowadza się do tego że i tak muszę odczytać wszystkie pola z klasy (ich nazwy i wartości) a jeśli są tam pola generyczne to też muszę odczytać ich wartości.
Czy pisząc metodę, która to robi w każdej klasie będzie to prostsze? pytam bo czegoś chyba nie rozumiem.

ŁF napisał(a):

Teraz mi jeszcze przyszło do głowy, że możesz użyć dynamic, wtedy nie potrzebujesz generyka, ani nawet refleksji, po prostu odwołujesz się do pola jak do słownika - dynamicObject[fieldName]. Zresztą, umówmy się, nie potrzebowałeś generyka od początku, mogłeś przyjmować jako argument object.

Muszę doczytać o dynamic bo go jeszcze nie używałem.

Podsumowując, spróbuję do klasy dodać metodę, która zwróci nazwę pola i wartość, może coś mi się po drodze wyjaśni.

1
Janko M. napisał(a):
Riddle napisał(a):

To czemu nie zrobisz że te obiekty różnych klasa mają wspólny podtym (wspólny interfejs albo wspólna klasa bazowa), i nie wyciągniesz odpowiednich danych przez metodę lub metodę abstrakcyjną?
To byłoby dużo lepsze wyjście niż czytanie pól po nazwie.

Problem sprowadza się do tego że i tak muszę odczytać wszystkie pola z klasy (ich nazwy i wartości) a jeśli są tam pola generyczne to też muszę odczytać ich wartości.

Wcale nie musisz, bo taka idea jak "odczytanie pola z klasy" to jest implementacja, więc nic nie musisz. Wymyśliłeś sobie takie rozwiązanie, ale ono nie jest dobre.

Janko M. napisał(a):

Czy pisząc metodę, która to robi w każdej klasie będzie to prostsze? pytam bo czegoś chyba nie rozumiem.

Prostsze raczej nie; ale trudniejsze też nie.

Po prostu wywołanie funkcji zamiast pola.

ŁF napisał(a):

Teraz mi jeszcze przyszło do głowy, że możesz użyć dynamic, wtedy nie potrzebujesz generyka, ani nawet refleksji, po prostu odwołujesz się do pola jak do słownika - dynamicObject[fieldName]. Zresztą, umówmy się, nie potrzebowałeś generyka od początku, mogłeś przyjmować jako argument object.

Muszę doczytać o dynamic bo go jeszcze nie używałem.

Podsumowując, spróbuję do klasy dodać metodę, która zwróci nazwę pola i wartość, może coś mi się po drodze wyjaśni.

Przestań odczytywać "pola" i po prostu zwróć wartości które potrzebujesz zwrócić.

Najlepiej by było, jakbyś po prostu pokazał Foo i to jak tworzysz te klasy. Nie dajesz sobie pomóc.

2

To nie ma sensu:

.GetFields()
.Where(field => field.IsPublic)

bo GetFields() zwraca tylko publiczne pola.

Ogólnie zamiast cudować z LINQ prawdopodobnie prościej type.GetField("name2").GetValue(rec).

0

Skończyło się na takim rozwiązaniu:

foreach (Object obj in records)
{
    Type type = obj.GetType();

    foreach (var f in type.GetFields())
    {
        string fName = f.Name;
        Object fValue = f.GetValue(obj);
	}
}

Potrzebuję nazwy wszystkich pól i wartości bo je raportuje, wszystkie albo na żądanie wybrane.
Dziękuję za wsparcie i sugestie.
Pozdrawiam.

0

Coś mi się wydaje że OP potrzebuje czegoś w stylu:

public static List<Dictionary<string, string>> drecords = new List<Dictionary<string, string>>()
{
    new Dictionary<string, string>() { {"name1","A"}, {"name2","a"}, },
    new Dictionary<string, string>() { {"name2","b"}, {"mail","B@b"}, },
    new Dictionary<string, string>() { {"pesel", "ccc"}, {"name2","c"}, },
    new Dictionary<string, string>() { {"name2","d"}, },
};

foreach(var rec in drecords) // name2 only
{
    if(rec.ContainsKey("name2")) Console.WriteLine(rec["name2"]);
    else Console.WriteLine("- none -");
}
foreach (var rec in drecords) // show all
{
    foreach(var field in rec) Console.WriteLine(field.Key+" => "+field.Value);
    Console.WriteLine();
}
0
_13th_Dragon napisał(a):

Coś mi się wydaje że OP potrzebuje czegoś w stylu:

        public static List<Dictionary<string, string>> drecords = new List<Dictionary<string, string>>()
        {
            new Dictionary<string, string>() { {"name1","A"}, {"name2","a"}, },
            new Dictionary<string, string>() { {"name2","b"}, {"mail","B@b"}, },
            new Dictionary<string, string>() { {"pesel", "ccc"}, {"name2","c"}, },
            new Dictionary<string, string>() { {"name2","d"}, },
        };

            foreach(var rec in drecords) // name2 only
            {
                if(rec.ContainsKey("name2")) Console.WriteLine(rec["name2"]);
                else Console.WriteLine("- none -");
            }
            foreach (var rec in drecords) // show all
            {
                foreach(var field in rec) Console.WriteLine(field.Key+" => "+field.Value);
                Console.WriteLine();
            }

Zgadza się, pod warunkiem że znasz nazwy wszystkich pól.
W metodzie, której raportuję pola i ich wartości takiej wiedzy nie mam.

0
Janko M. napisał(a):

Zgadza się, pod warunkiem że znasz nazwy wszystkich pól.
W metodzie, której raportuję pola i ich wartości takiej wiedzy nie mam.

Masz:

foreach (var rec in drecords)
{
  var keys=rec.Keys;
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1