Apakah ada cara yang lebih baik untuk mendapatkan nama Properti saat dilewatkan melalui ekspresi lambda? Inilah yang saya miliki saat ini.
misalnya.
GetSortingInfo<User>(u => u.UserId);
Itu bekerja dengan melemparkannya sebagai ekspresi anggota hanya ketika properti itu adalah string. karena tidak semua properti adalah string saya harus menggunakan objek tetapi kemudian akan mengembalikan unaryexpression untuk itu.
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
sumber
sumber
MemberExpression
pendekatan yang tercantum di sini hanya untuk mendapatkan nama anggota, bukan untuk mendapatkan yang sebenarnyaMemberInfo
, karena yangMemberInfo
dikembalikan tidak dijamin dari tipe yang tercermin dalam skenario "dervied: base" tertentu. Lihat lambda-ekspresi-tidak-kembali-diharapkan-anggota-info . Membuatku tersandung sekali. Jawaban yang diterima juga menderita dari ini.Jawaban:
Saya baru-baru ini melakukan hal yang sangat mirip untuk membuat metode OnPropertyChanged jenis yang aman.
Berikut adalah metode yang akan mengembalikan objek PropertyInfo untuk ekspresi. Itu melempar pengecualian jika ekspresi bukan properti.
The
source
parameter digunakan sehingga compiler dapat melakukan inferensi tipe pada metode panggilan. Anda dapat melakukan hal berikutsumber
u => u.OtherType.OtherTypesProperty
akan membuat kasus bahwa pernyataan terakhir sedang memeriksa.if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType) && !propInfo.ReflectedType.IsAssignableFrom(type))
untuk memungkinkan antarmuka juga.if(!propInfo.ReflectedType.IsAssignableFrom(type))
?Saya menemukan cara lain yang dapat Anda lakukan adalah memiliki sumber dan properti sangat diketik dan secara eksplisit menyimpulkan input untuk lambda. Tidak yakin apakah itu terminologi yang benar tetapi inilah hasilnya.
Dan menyebutnya seperti itu.
dan voila itu bekerja.
Terima kasih semuanya.
sumber
GetInfo(nameof(u.UserId))
var name = ((MemberExpression) ((UnaryExpression) accessor.Body).Operand).Member.Name
Saya bermain-main dengan hal yang sama dan menyelesaikannya. Ini tidak sepenuhnya diuji tetapi tampaknya menangani masalah dengan tipe nilai (masalah unaryexpression yang Anda temui)
sumber
o => o.Thing1.Thing2
akan kembaliThing2
, tidakThing1.Thing2
, yang tidak benar jika Anda mencoba menggunakannya di EntityFramework termasukIni menangani ekspresi anggota dan unary. Perbedaannya adalah bahwa Anda akan mendapatkan
UnaryExpression
jika ekspresi Anda mewakili tipe nilai sedangkan Anda akan mendapatkanMemberExpression
jika ekspresi Anda mewakili tipe referensi. Semuanya bisa dilemparkan ke objek, tetapi tipe nilai harus kotak. Inilah sebabnya mengapa UnaryExpression ada. Referensi.Demi keterbacaan (@Jowen), berikut ini ekuivalen yang diperluas:
sumber
Dengan pencocokan pola C # 7:
Contoh:
[Perbarui] pencocokan pola C # 8:
sumber
sekarang di C # 6 Anda cukup menggunakan nama seperti ini
nameof(User.UserId)
yang memiliki banyak manfaat, di antaranya adalah hal ini dilakukan pada waktu kompilasi , bukan runtime.
https://msdn.microsoft.com/en-us/magazine/dn802602.aspx
sumber
Ini adalah implementasi umum untuk mendapatkan nama string bidang / properti / pengindeks / metode / metode ekstensi / delegasi dari struct / class / interface / delegate / array. Saya telah menguji dengan kombinasi varian static / instance dan non-generik / generik.
Hal ini dapat ditulis dalam satu
while
loop sederhana juga:Saya suka pendekatan rekursif, meskipun yang kedua mungkin lebih mudah dibaca. Orang bisa menyebutnya seperti:
untuk mencetak anggota terakhir.
catatan:
Dalam kasus ekspresi berantai seperti
A.B.C
, "C" dikembalikan.Ini tidak berfungsi dengan
const
s, indexer array atauenum
s (tidak mungkin untuk mencakup semua kasus).sumber
Ada kasus tepi ketika datang ke
Array
. Panjang. Meskipun 'Panjang' ditampilkan sebagai properti, Anda tidak dapat menggunakannya di salah satu solusi yang diusulkan sebelumnya.Sekarang contoh penggunaan:
Jika
PropertyNameFromUnaryExpr
tidak memeriksaArrayLength
, "someArray" akan dicetak ke konsol (kompiler tampaknya menghasilkan akses langsung ke bidang Panjang dukungan , sebagai pengoptimalan, bahkan di Debug, dengan demikian kasus khusus).sumber
Berikut ini adalah pembaruan untuk metode yang diusulkan oleh Cameron . Parameter pertama tidak diperlukan.
Anda dapat melakukan hal berikut:
Metode ekstensi:
Kamu bisa:
sumber
u
sebagai tipe tertentu, dia tidak bisa melakukan itu karena tidak ada tipe untuk menyimpulkan. Apa yang dapat Anda lakukan adalahGetPropertyInfo<SomeType>(u => u.UserID)
Saya telah menemukan bahwa beberapa jawaban yang disarankan yang menelusuri ke
MemberExpression
/UnaryExpression
tidak menangkap bersarang / subproperti.ex)
o => o.Thing1.Thing2
mengembalikanThing1
daripadaThing1.Thing2
.Perbedaan ini penting jika Anda mencoba untuk bekerja dengan EntityFramework
DbSet.Include(...)
.Saya telah menemukan bahwa hanya mengurai
Expression.ToString()
tampaknya berfungsi dengan baik, dan relatif cepat. Saya membandingkannya denganUnaryExpression
versi, dan bahkanToString
turun dariMember/UnaryExpression
untuk melihat apakah itu lebih cepat, tetapi perbedaannya dapat diabaikan. Harap perbaiki saya jika ini adalah ide yang buruk.Metode Perpanjangan
(Memeriksa pembatas bahkan mungkin berlebihan)
Demo (LinqPad)
Demonstrasi + Kode perbandingan - https://gist.github.com/zaus/6992590
sumber
o => o.Thing1.Thing2
tidak kembaliThing1
seperti yang Anda katakan tetapiThing2
. Sebenarnya jawaban Anda mengembalikan sesuatu sepertiThing1.Thing2
yang mungkin atau mungkin tidak diinginkan.Thing1.Thing2
, tidak pernahThing1
. Saya mengatakanThing2
yang berarti nilai dario.Thing1.Thing2
, yang merupakan titik predikat. Saya akan memperbarui jawaban untuk mencerminkan niat itu.Thing1
? Saya tidak berpikir itu mengembalikannya sama sekali.Saya menggunakan metode ekstensi untuk proyek pra C # 6 dan nama () untuk mereka yang menargetkan C # 6.
Dan saya menyebutnya seperti:
Ini berfungsi baik dengan bidang dan properti.
sumber
Yah, tidak perlu menelepon
.Name.ToString()
, tapi secara luas itu saja, ya. Satu-satunya pertimbangan yang mungkin Anda butuhkan adalah apakahx.Foo.Bar
harus mengembalikan "Foo", "Bar", atau pengecualian - yaitu apakah Anda perlu mengulangi sama sekali.(komentar ulang) untuk informasi lebih lanjut tentang penyortiran fleksibel, lihat di sini .
sumber
ToString
harus memberikan hasil yang buruk untuk ekspresi unary.Saya membuat metode ekstensi pada ObjectStateEntry untuk dapat menandai properti (dari kelas POCO Entity Framework) yang dimodifikasi dalam jenis yang aman, karena metode default hanya menerima string. Inilah cara saya mendapatkan nama dari properti:
sumber
Saya telah melakukan
INotifyPropertyChanged
implementasi yang mirip dengan metode di bawah ini. Di sini properti disimpan dalam kamus di kelas dasar yang ditunjukkan di bawah ini. Tentu saja tidak selalu diinginkan untuk menggunakan warisan, tetapi untuk model tampilan saya pikir itu dapat diterima dan memberikan referensi properti yang sangat bersih di kelas model tampilan.Kelas dasar yang agak lebih kompleks ditunjukkan di bawah ini. Ini menangani terjemahan dari ekspresi lambda ke nama properti. Perhatikan bahwa properti tersebut adalah properti semu karena hanya nama yang digunakan. Tetapi akan terlihat transparan untuk model tampilan dan referensi ke properti pada model tampilan.
sumber
public bool IsLoading { get { return GetValue(MethodBase.GetCurrentMethod().Name); } set { SetPropertyValue(MethodBase.GetCurrentMethod().Name, value); } }
. Bisa lebih lambat, tetapi lebih umum dan mudah.Ini jawaban lain:
sumber
ModelMetadata
ada diSystem.Web.Mvc
namespace. Mungkin tidak cocok untuk kasus umumSaya meninggalkan fungsi ini jika Anda ingin mendapatkan beberapa bidang:
sumber
Berikut adalah cara lain untuk mendapatkan PropertyInfo berdasarkan jawaban ini. Ini menghilangkan kebutuhan akan instance objek.
Ini bisa disebut seperti ini:
sumber
Saya telah memperbarui jawaban @ Cameron untuk memasukkan beberapa pemeriksaan keamanan terhadap
Convert
ekspresi lambda yang diketik:sumber
Mulai dengan .NET 4.0 yang dapat Anda gunakan
ExpressionVisitor
untuk menemukan properti:Inilah cara Anda menggunakan pengunjung ini:
sumber
Ini mungkin optimal
sumber
sumber