Jak uzyskać dostęp do właściwości typu anonimowego w C #?
125
Mam to:
List<object> nodes =newList<object>();
nodes.Add(new{Checked=false,
depth =1,
id ="div_"+ d.Id});
... i zastanawiam się, czy mogę wtedy pobrać właściwość „Checked” anonimowego obiektu. Nie jestem pewien, czy jest to w ogóle możliwe. Próbowałem to zrobić:
if (nodes.Any(n => n["Checked"] == false)) ... ale to nie działa.
Jeśli chcesz mieć silnie wpisaną listę typów anonimowych, musisz również uczynić ją typem anonimowym. Najłatwiej to zrobić, rzutując sekwencję, taką jak tablica, na listę, np
var nodes =(new[]{new{Checked=false,/* etc */}}).ToList();
Wtedy będziesz mógł uzyskać do niego dostęp na przykład:
nodes.Any(n => n.Checked);
Ze względu na sposób działania kompilatora poniższe czynności powinny również działać po utworzeniu listy, ponieważ typy anonimowe mają tę samą strukturę, więc są również tego samego typu. Nie mam jednak pod ręką kompilatora, który mógłby to zweryfikować.
Jeśli przechowujesz obiekt jako typ object, musisz użyć odbicia. Dotyczy to każdego typu obiektu, anonimowego lub innego. Na obiekcie o możesz pobrać jego typ:
Type t = o.GetType();
Następnie wyszukujesz nieruchomość:
PropertyInfo p = t.GetProperty("Foo");
Następnie możesz uzyskać wartość:
object v = p.GetValue(o,null);
Ta odpowiedź jest od dawna oczekiwana na aktualizację dla C # 4:
dynamic d = o;object v = d.Foo;
A teraz kolejna alternatywa w C # 6:
object v = o?.GetType().GetProperty("Foo")?.GetValue(o,null);
Zauważ, że używając ?., sprawiamy, że wynik vznajduje się nullw trzech różnych sytuacjach!
ojest null, więc nie ma żadnego obiektu
onie jest, nullale nie ma właściwościFoo
oma własność, Fooale tak się właśnie dzieje null.
Nie jest to więc odpowiednik wcześniejszych przykładów, ale może mieć sens, jeśli chcesz traktować wszystkie trzy przypadki w ten sam sposób.
Nigdy wcześniej nie korzystałem z dynamiki, niezła aktualizacja .NET 4.0
Alan,
w rozwiązaniu C # 4 otrzymasz wyjątek czasu wykonywania, jeśli właściwość nie istnieje ( object v = d.Foo), natomiast GetValue(o, null)będzie miała wartość null, jeśli nie istnieje.
YaakovHatam
1
Nie, GetPropertyzwróci nulli GetValuewyrzuci, jeśli zostanie przekazany null, więc ogólny efekt jest wyjątkiem. Wersja C # 4.0 zawiera bardziej opisowy wyjątek.
Daniel Earwicker,
4
Jeśli używasz dynamiki w innym zestawie niż źródło, musisz użyć [InternalsVisibleTo]
Sarath
2
@DanielEarwicker dzięki za ukończenie. Dotyczy to również typów anonimowych. Ponieważ wszystkie właściwości wygenerowane dla typów anonimowych są wewnętrzne.
Sarath
13
Możesz iterować po właściwościach typu anonimowego używając Reflection; sprawdź, czy istnieje właściwość „Zaznaczona”, a jeśli tak, pobierz jej wartość.
foreach(object o in nodes){Type t = o.GetType();PropertyInfo[] pi = t.GetProperties();foreach(PropertyInfo p in pi){if(p.Name=="Checked"&&!(bool)p.GetValue(o))Console.WriteLine("awesome!");}}
Jeśli potrzebujesz tylko jednej nieruchomości i znasz już jej nazwę, nie ma sensu przeglądać ich wszystkich; po prostu użyj GetProperty i GetValue. Ponadto System.out.println to Java, a nie C # ...
Chris Charabaruk,
Ups, tak jest, Chris! Trochę zawstydzające ... naprawione teraz.
glennkentwell
6
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 =newList<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 Checkedwłaściwości i zgłasza RuntimeBinderExceptionw tym przypadku - więc jeśli spróbujesz użyć nieistniejącej Checked2wł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, UnanonymizePropertiesa UnanonymizeListItemsjako 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 ifwarunku, 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 KeyNotFoundExceptionwystę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:
publicstaticclassAnonymousTypeExtensions{// makes properties of object accessible publicstaticIDictionaryUnanonymizeProperties(thisobject 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 filterCriteriapublicstaticList<IDictionary>UnanonymizeListItems(thisList<object> objectList,Func<IDictionary<string,object>,bool> filterCriteria=default){var accessibleList =newList<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>publicstaticobjectGetProp(thisobject obj,string propertyName,bool treatNotFoundAsNull =false){try{return((System.Collections.Generic.IDictionary<string,object>)obj)?[propertyName];}catch(KeyNotFoundException){if(treatNotFoundAsNull)returndefault(object);elsethrow;}}}
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ć.
object v = d.Foo
), natomiastGetValue(o, null)
będzie miała wartość null, jeśli nie istnieje.GetProperty
zwrócinull
iGetValue
wyrzuci, jeśli zostanie przekazanynull
, więc ogólny efekt jest wyjątkiem. Wersja C # 4.0 zawiera bardziej opisowy wyjątek.Możesz iterować po właściwościach typu anonimowego używając Reflection; sprawdź, czy istnieje właściwość „Zaznaczona”, a jeśli tak, pobierz jej wartość.
Zobacz ten post na blogu: http://blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx
Więc coś takiego:
źródło
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 przeznew { @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
:1. Rozwiązanie z dynamiką
W C # 4.0 i wyższych wersjach możesz po prostu rzutować na dynamiczny i pisać:
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łaszaRuntimeBinderException
w tym przypadku - więc jeśli spróbujesz użyć nieistniejącejChecked2
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
aUnanonymizeListItems
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:lub możesz użyć wyrażenia
nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
jakoif
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ć:
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:
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:
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.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ć.
źródło
Ostatnio miałem ten sam problem w .NET 3.5 (brak dynamicznej wersji). Oto jak rozwiązałem:
Zaadaptowano gdzieś na stackoverflow:
Teraz odzyskaj obiekt przez rzut:
źródło