Przyjęta odpowiedź poprawnie opisuje sposób deklaracji listy i jest wysoce zalecana dla większości scenariuszy.
Ale trafiłem na inny scenariusz, który obejmuje również zadane pytanie. Co jeśli musisz użyć istniejącej listy obiektów, jak ViewData["htmlAttributes"]
w MVC ? Jak uzyskać dostęp do jego właściwości (są one zwykle tworzone przez new { @style="width: 100px", ... }
)?
W przypadku tego nieco innego scenariusza chcę się z wami podzielić tym, czego się dowiedziałem. W poniższych rozwiązaniach przyjmuję następującą deklarację dla nodes
:
List<object> nodes = new List<object>();
nodes.Add(
new
{
Checked = false,
depth = 1,
id = "div_1"
});
1. Rozwiązanie z dynamiką
W C # 4.0 i wyższych wersjach możesz po prostu rzutować na dynamiczny i pisać:
if (nodes.Any(n => ((dynamic)n).Checked == false))
Console.WriteLine("found not checked element!");
Uwaga: to używa późnego wiązania, co oznacza, że będzie rozpoznawać tylko w czasie wykonywania, jeśli obiekt nie ma Checked
właściwości i zgłasza RuntimeBinderException
w tym przypadku - więc jeśli spróbujesz użyć nieistniejącej Checked2
właściwości, otrzymasz następujący komunikat o Czas trwania: "'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
.
2. Rozwiązanie z refleksją
Rozwiązanie z odbiciem działa zarówno ze starymi, jak i nowymi wersjami kompilatora C # . W przypadku starszych wersji języka C # należy zapoznać się ze wskazówką na końcu tej odpowiedzi.
tło
Jako punkt wyjścia, znalazłem dobrej odpowiedzi tutaj . Pomysł polega na przekonwertowaniu anonimowego typu danych na słownik przy użyciu odbicia. Słownik ułatwia dostęp do właściwości, ponieważ ich nazwy są przechowywane jako klucze (można uzyskać do nich dostęp np myDict["myProperty"]
.).
Zainspirowany kodu w linku powyżej, stworzyłem klasę wewnętrzny zapewniający GetProp
, UnanonymizeProperties
a UnanonymizeListItems
jako metod rozszerzenie, które upraszczają dostęp do właściwości anonimowych. Dzięki tej klasie możesz po prostu wykonać zapytanie w następujący sposób:
if (nodes.UnanonymizeListItems().Any(n => (bool)n["Checked"] == false))
{
Console.WriteLine("found not checked element!");
}
lub możesz użyć wyrażenia nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
jako if
warunku, który filtruje niejawnie, a następnie sprawdza, czy zostały zwrócone jakieś elementy.
Aby pobrać pierwszy obiekt zawierający właściwość „Checked” i zwrócić jego właściwość „depth”, możesz użyć:
var depth = nodes.UnanonymizeListItems()
?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");
lub krócej: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
Uwaga: jeśli masz listę obiektów, które niekoniecznie zawierają wszystkie właściwości (na przykład niektóre nie zawierają właściwości „Zaznaczone”) i nadal chcesz utworzyć zapytanie w oparciu o wartości „Zaznaczone”, możesz Zrób to:
if (nodes.UnanonymizeListItems(x => { var y = ((bool?)x.GetProp("Checked", true));
return y.HasValue && y.Value == false;}).Any())
{
Console.WriteLine("found not checked element!");
}
Zapobiega to KeyNotFoundException
występowaniu a, jeśli właściwość „Checked” nie istnieje.
Poniższa klasa zawiera następujące metody rozszerzające:
UnanonymizeProperties
: Służy do usuwania anonimowości właściwości zawartych w obiekcie. Ta metoda wykorzystuje odbicie. Konwertuje obiekt na słownik zawierający właściwości i ich wartości.
UnanonymizeListItems
: Służy do konwersji listy obiektów na listę słowników zawierających właściwości. Może opcjonalnie zawierać wyrażenie lambda do wcześniejszego filtrowania .
GetProp
: Służy do zwracania pojedynczej wartości pasującej do podanej nazwy właściwości. Umożliwia traktowanie nieistniejących właściwości jako wartości null (true), a nie jako KeyNotFoundException (false)
W przypadku powyższych przykładów wystarczy dodać poniższą klasę rozszerzenia:
public static class AnonymousTypeExtensions
{
// makes properties of object accessible
public static IDictionary UnanonymizeProperties(this object obj)
{
Type type = obj?.GetType();
var properties = type?.GetProperties()
?.Select(n => n.Name)
?.ToDictionary(k => k, k => type.GetProperty(k).GetValue(obj, null));
return properties;
}
// converts object list into list of properties that meet the filterCriteria
public static List<IDictionary> UnanonymizeListItems(this List<object> objectList,
Func<IDictionary<string, object>, bool> filterCriteria=default)
{
var accessibleList = new List<IDictionary>();
foreach (object obj in objectList)
{
var props = obj.UnanonymizeProperties();
if (filterCriteria == default
|| filterCriteria((IDictionary<string, object>)props) == true)
{ accessibleList.Add(props); }
}
return accessibleList;
}
// returns specific property, i.e. obj.GetProp(propertyName)
// requires prior usage of AccessListItems and selection of one element, because
// object needs to be a IDictionary<string, object>
public static object GetProp(this object obj, string propertyName,
bool treatNotFoundAsNull = false)
{
try
{
return ((System.Collections.Generic.IDictionary<string, object>)obj)
?[propertyName];
}
catch (KeyNotFoundException)
{
if (treatNotFoundAsNull) return default(object); else throw;
}
}
}
Podpowiedź: powyższy kod używa operatorów warunkowych zerowych , dostępnych od wersji C # 6.0 - jeśli pracujesz ze starszymi kompilatorami C # (np. C # 3.0), po prostu zamień ?.
na .
i ?[
przez [
wszędzie, np.
var depth = nodes.UnanonymizeListItems()
.FirstOrDefault(n => n.Contains("Checked"))["depth"];
Jeśli nie zmuszony do korzystania ze starszej kompilatora C #, aby go jak jest, bo przy użyciu null warunkowe sprawia NULL obsługi znacznie łatwiejsze.
Uwaga: Podobnie jak inne rozwiązanie z dynamiką, to rozwiązanie również używa późnego wiązania, ale w tym przypadku nie otrzymujesz wyjątku - po prostu nie znajdzie elementu, jeśli odnosisz się do nieistniejącej właściwości, o ile zachowując zerowe operatory warunkowe .
W przypadku niektórych aplikacji przydatne może być odwołanie się do właściwości za pośrednictwem ciągu znaków w rozwiązaniu 2, dlatego można ją sparametryzować.