Czy istnieje lepszy sposób na uzyskanie nazwy właściwości po przekazaniu za pomocą wyrażenia lambda? Oto, co aktualnie mam.
na przykład.
GetSortingInfo<User>(u => u.UserId);
Działa poprzez rzutowanie go jako wyrażenia membranowego tylko wtedy, gdy właściwość jest ciągiem znaków. ponieważ nie wszystkie właściwości są łańcuchami, musiałem użyć obiektu, ale wtedy zwróciłby dla nich jednoargumentowe wyrażenie.
public static RouteValueDictionary GetInfo<T>(this HtmlHelper html,
Expression<Func<T, object>> action) where T : class
{
var expression = GetMemberInfo(action);
string name = expression.Member.Name;
return GetInfo(html, name);
}
private static MemberExpression GetMemberInfo(Expression method)
{
LambdaExpression lambda = method as LambdaExpression;
if (lambda == null)
throw new ArgumentNullException("method");
MemberExpression memberExpr = null;
if (lambda.Body.NodeType == ExpressionType.Convert)
{
memberExpr =
((UnaryExpression)lambda.Body).Operand as MemberExpression;
}
else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpr = lambda.Body as MemberExpression;
}
if (memberExpr == null)
throw new ArgumentException("method");
return memberExpr;
}
c#
linq
lambda
expression-trees
Schotime
źródło
źródło
MemberExpression
wymienione tutaj podejście tylko do uzyskania nazwy członka, a nie do uzyskaniaMemberInfo
samego samego, ponieważMemberInfo
nie ma gwarancji , że zwracany typ będzie odzwierciedlony w niektórych scenariuszach „dervied: base”. Zobacz lambda-expression-not-return-expect-memberinfo . Potknąłem się raz. Cierpi na to również zaakceptowana odpowiedź.Odpowiedzi:
Niedawno zrobiłem bardzo podobną rzecz, aby uczynić typ bezpieczną metodą OnPropertyChanged.
Oto metoda, która zwróci obiekt PropertyInfo dla wyrażenia. Zgłasza wyjątek, jeśli wyrażenie nie jest właściwością.
Ten
source
parametr jest używany, aby kompilator mógł wnioskować o typie w wywołaniu metody. Możesz wykonać następujące czynnościźródło
u => u.OtherType.OtherTypesProperty
stworzy taki przypadek, który sprawdza ostatnia instrukcja.if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType) && !propInfo.ReflectedType.IsAssignableFrom(type))
aby umożliwić także interfejsy.if(!propInfo.ReflectedType.IsAssignableFrom(type))
?Znalazłem inny sposób, w jaki można to zrobić, polegający na silnym wpisaniu źródła i właściwości oraz jawnym wywnioskowaniu danych wejściowych dla lambda. Nie jestem pewien, czy jest to poprawna terminologia, ale oto wynik.
A potem tak to nazwać.
i voila to działa.
Dziękuje wszystkim.
źródło
GetInfo(nameof(u.UserId))
var name = ((MemberExpression) ((UnaryExpression) accessor.Body).Operand).Member.Name
Bawiłem się tym samym i pracowałem nad tym. Nie jest w pełni przetestowany, ale wydaje się, że radzi sobie z problemem z typami wartości (problem z jednorazowym wyrażeniem, na jaki się natknąłeś)
źródło
o => o.Thing1.Thing2
zwróciThing2
, nieThing1.Thing2
, co jest niepoprawne, jeśli próbujesz użyć go w EntityFramework obejmujeObsługuje wyrażenia elementarne i jednoargumentowe. Różnica polega na tym, że otrzymasz
UnaryExpression
wyrażenie, jeśli wyrażenie reprezentuje typ wartości, natomiast otrzymaszMemberExpression
wyrażenie, jeśli wyrażenie reprezentuje typ odwołania. Wszystko można rzutować na obiekt, ale typy wartości muszą być zapakowane w ramki. Właśnie dlatego istnieje UnaryExpression. Odniesienie.Ze względu na czytelność (@Jowen), oto rozwinięty odpowiednik:
źródło
Z dopasowaniem wzoru C # 7:
Przykład:
[Aktualizacja] Dopasowanie wzorca C # 8:
źródło
teraz w C # 6 możesz po prostu użyć nameof w ten sposób
nameof(User.UserId)
co ma wiele zalet, między innymi dlatego, że odbywa się to w czasie kompilacji , a nie w czasie wykonywania.
https://msdn.microsoft.com/en-us/magazine/dn802602.aspx
źródło
Jest to ogólna implementacja służąca do uzyskania nazwy ciągu pól / właściwości / indeksatorów / metod / metod rozszerzenia / delegatów struct / class / interface / delegate / array. Testowałem z kombinacjami wariantów statycznych / instancji i nietypowych / ogólnych.
To też można napisać w prostej
while
pętli:Lubię podejście rekurencyjne, chociaż drugie może być łatwiejsze do odczytania. Można to nazwać tak:
aby wydrukować ostatniego członka.
Uwaga:
W przypadku takich wyrażeń łańcuchowych, jak
A.B.C
„C” jest zwracane.Nie działa to z
const
s, indeksatorami tablic lubenum
s (niemożliwe jest uwzględnienie wszystkich przypadków).źródło
Istnieje przypadek na krawędzi, jeśli chodzi o
Array
.Length. Chociaż „Długość” jest widoczna jako właściwość, nie można jej używać w żadnym z wcześniej proponowanych rozwiązań.Teraz przykładowe użycie:
Gdyby
PropertyNameFromUnaryExpr
nie sprawdziłArrayLength
, „someArray” zostałby wydrukowany na konsoli (kompilator wydaje się generować bezpośredni dostęp do pola Długość kopii zapasowej , jako optymalizacja, nawet w Debugowaniu, a więc w specjalnym przypadku).źródło
Oto aktualizacja metody zaproponowanej przez Camerona . Pierwszy parametr nie jest wymagany.
Możesz wykonać następujące czynności:
Metody rozszerzenia:
Możesz:
źródło
u
jako jakiś typ, nie może tego zrobić, ponieważ nie ma typu do wnioskowania. Co możesz zrobić, toGetPropertyInfo<SomeType>(u => u.UserID)
Odkryłem, że niektóre z sugerowanych odpowiedzi, które przechodzą do
MemberExpression
/UnaryExpression
nie przechwytują zagnieżdżonych / pod właściwości.ex)
o => o.Thing1.Thing2
zwracaThing1
raczej niżThing1.Thing2
.To rozróżnienie jest ważne, jeśli próbujesz pracować z EntityFramework
DbSet.Include(...)
.Przekonałem się, że samo parsowanie
Expression.ToString()
wydaje się działać dobrze i stosunkowo szybko. Porównałem go zUnaryExpression
wersją, a nawet zszedłemToString
z tego,Member/UnaryExpression
aby sprawdzić, czy to było szybsze, ale różnica była znikoma. Popraw mnie, jeśli to okropny pomysł.Metoda rozszerzenia
(Sprawdzanie separatora może być nawet nadmierne)
Demo (LinqPad)
Demonstracja + kod porównawczy - https://gist.github.com/zaus/6992590
źródło
o => o.Thing1.Thing2
, nie powraca,Thing1
jak mówisz, aleThing2
. W rzeczywistości twoja odpowiedź zwraca coś,Thing1.Thing2
co może być pożądane lub nie.Thing1.Thing2
, nigdyThing1
. Powiedziałem,Thing2
co oznacza, że wartość zo.Thing1.Thing2
, co jest punktem orzecznika. Zaktualizuję odpowiedź, aby odzwierciedlić ten zamiar.Thing1
? Nie sądzę, żeby to w ogóle przestało działać.Używam metody rozszerzenia dla projektów sprzed C # 6 i nameof () dla tych, którzy celują w C # 6.
I nazywam to tak:
Działa dobrze zarówno z polami, jak i właściwościami.
źródło
Cóż, nie trzeba dzwonić
.Name.ToString()
, ale ogólnie o to chodzi, tak. Jedyne, czego możesz potrzebować, to czyx.Foo.Bar
powinieneś zwrócić „Foo”, „Bar” lub wyjątek - tj. Czy w ogóle musisz iterować.(ponownie komentarz), aby uzyskać więcej informacji na temat elastycznego sortowania, zobacz tutaj .
źródło
ToString
powinno dawać brzydkie wyniki dla wyrażeń jednoargumentowych.Utworzyłem metodę rozszerzenia na ObjectStateEntry, aby móc oflagować właściwości (klas Entity Framework POCO) zmodyfikowane w sposób bezpieczny dla typu, ponieważ metoda domyślna akceptuje tylko ciąg znaków. Oto mój sposób na uzyskanie nazwy z nieruchomości:
źródło
Wykonałem
INotifyPropertyChanged
implementację podobną do metody poniżej. Tutaj właściwości są przechowywane w słowniku w klasie podstawowej pokazanej poniżej. Oczywiście nie zawsze pożądane jest dziedziczenie, ale myślę, że w przypadku modeli widoku jest to dopuszczalne i daje bardzo czyste referencje właściwości w klasach modeli widoku.Nieco bardziej złożoną klasę podstawową pokazano poniżej. Obsługuje tłumaczenie z wyrażenia lambda na nazwę właściwości. Zauważ, że właściwości są naprawdę pseudo właściwościami, ponieważ używane są tylko nazwy. Ale będzie wyglądać przezroczyście dla modelu widoku i odniesień do właściwości w modelu widoku.
źródło
public bool IsLoading { get { return GetValue(MethodBase.GetCurrentMethod().Name); } set { SetPropertyValue(MethodBase.GetCurrentMethod().Name, value); } }
. Może być wolniejszy, ale bardziej ogólny i prosty.To kolejna odpowiedź:
źródło
ModelMetadata
istnieje wSystem.Web.Mvc
przestrzeni nazw. Może nie pasuje do ogólnego przypadkuOpuszczam tę funkcję, jeśli chcesz uzyskać wiele pól:
źródło
Oto inny sposób na uzyskanie właściwości PropertyInfo na podstawie tej odpowiedzi. Eliminuje to potrzebę wystąpienia obiektu.
Można to tak nazwać:
źródło
Zaktualizowałem odpowiedź @ Camerona, aby uwzględnić pewne kontrole bezpieczeństwa w stosunku do
Convert
wpisywanych wyrażeń lambda:źródło
Począwszy od .NET 4.0 można użyć
ExpressionVisitor
do znalezienia właściwości:Oto jak korzystasz z tego użytkownika:
źródło
To może być optymalne
źródło
źródło