Dapatkan nama properti sebagai string

204

(Lihat solusi di bawah ini yang saya buat menggunakan jawaban yang saya terima)

Saya mencoba untuk meningkatkan pemeliharaan dari beberapa kode yang melibatkan refleksi. Aplikasi ini memiliki antarmuka .NET Remoting mengekspos (antara lain) metode yang disebut Jalankan untuk mengakses bagian-bagian dari aplikasi yang tidak termasuk dalam antarmuka jarak jauh yang diterbitkan.

Berikut adalah cara aplikasi menentukan properti (yang statis dalam contoh ini) yang dimaksudkan agar dapat diakses melalui Execute:

RemoteMgr.ExposeProperty("SomeSecret", typeof(SomeClass), "SomeProperty");

Jadi pengguna jarak jauh dapat menelepon:

string response = remoteObject.Execute("SomeSecret");

dan aplikasi akan menggunakan refleksi untuk menemukan SomeClass.SomeProperty dan mengembalikan nilainya sebagai string.

Sayangnya, jika seseorang mengganti nama SomeProperty dan lupa untuk mengubah parm ke-3 dari ExposeProperty (), ia merusak mekanisme ini.

Saya perlu yang setara dengan:

SomeClass.SomeProperty.GetTheNameOfThisPropertyAsAString()

untuk digunakan sebagai parm ke-3 di ExposeProperty sehingga alat refactoring akan menangani penggantian nama.

Apakah ada cara untuk melakukan ini? Terima kasih sebelumnya.

Oke, inilah yang akhirnya saya buat (berdasarkan jawaban yang saya pilih dan pertanyaan yang dia referensikan):

// <summary>
// Get the name of a static or instance property from a property access lambda.
// </summary>
// <typeparam name="T">Type of the property</typeparam>
// <param name="propertyLambda">lambda expression of the form: '() => Class.Property' or '() => object.Property'</param>
// <returns>The name of the property</returns>
public string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
    var me = propertyLambda.Body as MemberExpression;

    if (me == null)
    {
        throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
    }

    return me.Member.Name;
 }

Pemakaian:

// Static Property
string name = GetPropertyName(() => SomeClass.SomeProperty);

// Instance Property
string name = GetPropertyName(() => someObject.SomeProperty);

Sekarang dengan kemampuan keren ini, saatnya menyederhanakan metode ExposeProperty. Memoles gagang pintu adalah pekerjaan berbahaya ...

Terimakasih semuanya.

Jim C
sumber
9
Benar-benar appriciated bahwa Anda menambahkan solusi Anda dan mengikat semuanya.
Cukup G.
Anda harus menambahkan solusi Anda sebagai jawaban - itu jauh lebih ringkas daripada jawaban yang Anda terima.
Kenny Evitt
1
@Kenny Evitt: Selesai:)
Jim C
@JimC Terpilih! Dan ditautkan dalam komentar pada jawaban yang saat ini diterima . Terima kasih!
Kenny Evitt

Jawaban:

61

Menggunakan GetMemberInfo dari sini: Mengambil nama Properti dari ekspresi lambda Anda dapat melakukan sesuatu seperti ini:

RemoteMgr.ExposeProperty(() => SomeClass.SomeProperty)

public class SomeClass
{
    public static string SomeProperty
    {
        get { return "Foo"; }
    }
}

public class RemoteMgr
{
    public static void ExposeProperty<T>(Expression<Func<T>> property)
    {
        var expression = GetMemberInfo(property);
        string path = string.Concat(expression.Member.DeclaringType.FullName,
            ".", expression.Member.Name);
        // Do ExposeProperty work here...
    }
}

public class Program
{
    public static void Main()
    {
        RemoteMgr.ExposeProperty("SomeSecret", () => SomeClass.SomeProperty);
    }
}
Daniel Renshaw
sumber
Itu sangat keren. Sepertinya itu akan bekerja pada semua tipe properti juga.
Jim C
Saya baru saja mencobanya dengan properti instance dan static. Sejauh ini baik.
Jim C
Adakah ide di mana saya bisa mendapatkan paket assembly atau NuGet yang berisi GetMemberInfo? Saya tidak dapat menemukan apa pun dengan paket 'utilitas umum' untuk Perpustakaan Microsoft Enterprise, yang sepertinya ditunjukkan oleh MSDN berisi metode itu. Ada paket "tidak resmi" tetapi tidak resmi tidak menginspirasi. Jawaban JimC , yang didasarkan pada yang satu ini, jauh lebih ringkas dan tidak bergantung pada perpustakaan yang tampaknya tidak tersedia.
Kenny Evitt
1
@ KennyEvitt, metode yang dia referensikan adalah yang ditulis oleh penulis pertanyaan yang dia tautkan. Alternatif untuk metode itu, Anda dapat menggunakan Tipe ini. Dapatkan anggota msdn.microsoft.com/en-us/library/…
Bon
464

Dengan C # 6.0, ini bukan masalah seperti yang dapat Anda lakukan:

nameof(SomeProperty)

Ungkapan ini diselesaikan pada saat kompilasi ke "SomeProperty".

Dokumentasi MSDN dari nama .

James Ko
sumber
18
Ini adalah badass dan sangat berguna untuk panggilan ModelState.AddModelError.
Michael Silver
9
Dan ini adalah const string! Luar biasa
Jack
4
@RaidenCore yakin, jika Anda menulis untuk mikrokontroler Anda harus menggunakan bahasa tingkat rendah seperti C, dan jika Anda perlu memeras setiap bit kinerja seperti pemrosesan gambar dan video, Anda harus menggunakan C atau C ++. tetapi untuk 95% aplikasi lainnya, kerangka kode yang dikelola akan cukup cepat. Akhirnya C # juga dikompilasi ke kode mesin, dan Anda bahkan dapat melakukan pra-kompilasi ke kode asli jika Anda mau.
Tsahi Asher
2
Omong-omong, @RaidenCore, aplikasi yang Anda sebutkan mendahului C #, itu sebabnya mereka ditulis dalam C ++. Jika ditulis hari ini, siapa yang tahu bahasa apa yang digunakan. Lihat misalnya Paint.NET.
Tsahi Asher
1
Ini sangat berguna ketika Anda ingin RaisePropertydi WPF! Gunakan RaisePropertyChanged (nameof (property)) alih-alih RaisePropertyChanged ("property")
Pierre
17

Ada peretasan terkenal untuk mengekstraknya dari ekspresi lambda (ini dari kelas PropertyObserver, oleh Josh Smith, di yayasan MVVM-nya):

    private static string GetPropertyName<TPropertySource>
        (Expression<Func<TPropertySource, object>> expression)
    {
        var lambda = expression as LambdaExpression;
        MemberExpression memberExpression;
        if (lambda.Body is UnaryExpression)
        {
            var unaryExpression = lambda.Body as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = lambda.Body as MemberExpression;
        }

        Debug.Assert(memberExpression != null, 
           "Please provide a lambda expression like 'n => n.PropertyName'");

        if (memberExpression != null)
        {
            var propertyInfo = memberExpression.Member as PropertyInfo;

            return propertyInfo.Name;
        }

        return null;
    }

Maaf, ini tidak ada konteks. Ini adalah bagian dari kelas yang lebih besar di mana TPropertySourcekelas yang berisi properti. Anda bisa membuat fungsi generik di TPropertySource untuk mengekstraknya dari kelas. Saya sarankan untuk melihat kode lengkap dari Yayasan MVVM .

Dan Bryant
sumber
Dengan contoh cara memanggil fungsi, ini tentu +1. Ups, tidak melihat bahwa ada satu di pernyataan debug - itu sebabnya membuat pengembang secara horizontal menggulir untuk sampai ke bagian penting dari garis itu jahat;)
OregonGhost
Hmmm ... Saya harus membedah yang ini untuk memahaminya.
Jim C
Visual Studio 2008 menandai "TPropertySource" sebagai kesalahan ("tidak dapat ditemukan").
Jim C
Saya baru menyadari bahwa nama jenisnya bukan hanya simbol <T> seperti pada C ++. Apa yang diwakili oleh TPropertySource?
Jim C
2
Untuk membuat kompilasi ini, Anda cukup mengubah tanda tangan metode untuk membaca public static string GetPropertyName<TPropertySource>(Expression<Func<TPropertySource, object>> expression)lalu memanggil seperti:var name = GetPropertyName<TestClass>(x => x.Foo);
dav_i
16

Oke, inilah yang akhirnya saya buat (berdasarkan jawaban yang saya pilih dan pertanyaan yang dia referensikan):

// <summary>
// Get the name of a static or instance property from a property access lambda.
// </summary>
// <typeparam name="T">Type of the property</typeparam>
// <param name="propertyLambda">lambda expression of the form: '() => Class.Property' or '() => object.Property'</param>
// <returns>The name of the property</returns>

public string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
    var me = propertyLambda.Body as MemberExpression;

    if (me == null)
    {
        throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
    }

    return me.Member.Name;
 }

Pemakaian:

// Static Property
string name = GetPropertyName(() => SomeClass.SomeProperty);

// Instance Property
string name = GetPropertyName(() => someObject.SomeProperty);
Jim C
sumber
8

Kelas PropertyInfo akan membantu Anda mencapai ini, jika saya mengerti dengan benar.

  1. Metode Type.GetProperties ()

    PropertyInfo[] propInfos = typeof(ReflectedType).GetProperties();
    propInfos.ToList().ForEach(p => 
        Console.WriteLine(string.Format("Property name: {0}", p.Name));

Apakah ini yang Anda butuhkan?

Will Marcouiller
sumber
Tidak, meskipun saya menggunakan GetProperties ketika aplikasi menerima permintaan untuk "SomeSecret". Aplikasi ini mencari "SomeSecret" di peta untuk menemukannya perlu menemukan properti yang disebut "SomeProperty" di kelas yang disebut "SomeClass".
Jim C
nameof (SomeProperty) sebenarnya memudahkan ini dari .net 4.0 dan seterusnya. Tidak perlu melakukan peretasan yang panjang.
Div Tiwari
6

Anda dapat menggunakan Refleksi untuk mendapatkan nama properti yang sebenarnya.

http://www.csharp-examples.net/reflection-property-names/

Jika Anda membutuhkan cara untuk menetapkan "Nama String" ke properti, mengapa Anda tidak menulis atribut yang dapat Anda pantulkan untuk mendapatkan nama string?

[StringName("MyStringName")]
private string MyProperty
{
    get { ... }
}
Robert Harvey
sumber
1
Ya, itulah cara aplikasi menangani permintaan masuk untuk "SomeSecret", tetapi itu tidak memberi saya alat untuk masalah ExposeProperty.
Jim C
Menarik ... maka Anda dapat mengubah nama MyProperty menjadi konten hati Anda selama Anda tidak mengacaukan MyStringName, dan jika karena alasan tertentu Anda ingin mengubahnya maka Anda perlu memodifikasi parm ExposeProperty. Setidaknya saya bisa menambahkan komentar di sebelah atribut dengan peringatan seperti itu karena Anda harus melihatnya untuk mengubah nilai atribut (tidak seperti mengubah nama properti, yang dapat dilakukan dari lokasi referensi mana pun).
Jim C
6

Saya memodifikasi solusi Anda untuk menghubungkan beberapa properti:

public static string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
    MemberExpression me = propertyLambda.Body as MemberExpression;
    if (me == null)
    {
        throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
    }

    string result = string.Empty;
    do
    {
        result = me.Member.Name + "." + result;
        me = me.Expression as MemberExpression;
    } while (me != null);

    result = result.Remove(result.Length - 1); // remove the trailing "."
    return result;
}

Pemakaian:

string name = GetPropertyName(() => someObject.SomeProperty.SomeOtherProperty);
// returns "SomeProperty.SomeOtherProperty"
manusia gila
sumber
4

Berdasarkan jawaban yang sudah ada dalam pertanyaan dan pada artikel ini: https://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/ I Saya mempresentasikan solusi saya untuk masalah ini:

public static class PropertyNameHelper
{
    /// <summary>
    /// A static method to get the Propertyname String of a Property
    /// It eliminates the need for "Magic Strings" and assures type safety when renaming properties.
    /// See: http://stackoverflow.com/questions/2820660/get-name-of-property-as-a-string
    /// </summary>
    /// <example>
    /// // Static Property
    /// string name = PropertyNameHelper.GetPropertyName(() => SomeClass.SomeProperty);
    /// // Instance Property
    /// string name = PropertyNameHelper.GetPropertyName(() => someObject.SomeProperty);
    /// </example>
    /// <typeparam name="T"></typeparam>
    /// <param name="propertyLambda"></param>
    /// <returns></returns>
    public static string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
    {
        var me = propertyLambda.Body as MemberExpression;

        if (me == null)
        {
            throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
        }

        return me.Member.Name;
    }
    /// <summary>
    /// Another way to get Instance Property names as strings.
    /// With this method you don't need to create a instance first.
    /// See the example.
    /// See: https://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/
    /// </summary>
    /// <example>
    /// string name = PropertyNameHelper((Firma f) => f.Firmenumsatz_Waehrung);
    /// </example>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="TReturn"></typeparam>
    /// <param name="expression"></param>
    /// <returns></returns>
    public static string GetPropertyName<T, TReturn>(Expression<Func<T, TReturn>> expression)
    {
        MemberExpression body = (MemberExpression)expression.Body;
        return body.Member.Name;
    }
}

Dan Tes yang juga menunjukkan penggunaan untuk properti contoh dan statis:

[TestClass]
public class PropertyNameHelperTest
{
    private class TestClass
    {
        public static string StaticString { get; set; }
        public string InstanceString { get; set; }
    }

    [TestMethod]
    public void TestGetPropertyName()
    {
        Assert.AreEqual("StaticString", PropertyNameHelper.GetPropertyName(() => TestClass.StaticString));

        Assert.AreEqual("InstanceString", PropertyNameHelper.GetPropertyName((TestClass t) => t.InstanceString));
    }
}
Thomas
sumber
3

Pertanyaan lama, tetapi jawaban lain untuk pertanyaan ini adalah membuat fungsi statis di kelas pembantu yang menggunakan CallerMemberNameAttribute.

public static string GetPropertyName([CallerMemberName] String propertyName = null) {
  return propertyName;
}

Dan kemudian gunakan seperti:

public string MyProperty {
  get { Console.WriteLine("{0} was called", GetPropertyName()); return _myProperty; }
}
Jim Pedid
sumber
0

Anda dapat menggunakan kelas StackTrace untuk mendapatkan nama fungsi saat ini, (atau jika Anda meletakkan kode dalam suatu fungsi, maka turunkan level dan dapatkan fungsi panggilan).

Lihat http://msdn.microsoft.com/en-us/library/system.diagnostics.stacktrace(VS.71).aspx

Bintik-bintik
sumber
Saya tidak tahu di mana Anda berada dalam pikiran untuk menangkap jejak tumpukan, tapi saya tidak bisa memikirkan satu yang akan berisi nama properti.
Jim C
Anda dapat melakukan ini, tetapi ini dapat mengarah pada hasil yang tidak terduga (termasuk pengecualian) karena kompilasi inlining optimasi. smelser.net/blog/post/2008/11/27/…
JoeGeeky
0

Saya telah menggunakan jawaban ini untuk efek yang sangat baik: Dapatkan properti, sebagai string, dari Ekspresi <Func <TModel, TProperty >>

Saya sadar saya sudah menjawab pertanyaan ini beberapa waktu lalu. Satu-satunya keuntungan jawaban saya yang lain adalah bahwa ia berfungsi untuk properti statis. Saya menemukan sintaks dalam jawaban ini jauh lebih berguna karena Anda tidak harus membuat variabel dari jenis yang ingin Anda refleksikan.

manusia gila
sumber
0

Saya mengalami beberapa kesulitan menggunakan solusi yang sudah disarankan untuk kasus penggunaan khusus saya, tetapi akhirnya menemukan jawabannya. Saya tidak berpikir kasus spesifik saya layak untuk pertanyaan baru, jadi saya memposting solusi saya di sini untuk referensi. (Ini sangat erat kaitannya dengan pertanyaan dan memberikan solusi bagi orang lain dengan kasus serupa dengan saya).

Kode saya berakhir dengan tampilan seperti ini:

public class HideableControl<T>: Control where T: class
{
    private string _propertyName;
    private PropertyInfo _propertyInfo;

    public string PropertyName
    {
        get { return _propertyName; }
        set
        {
            _propertyName = value;
            _propertyInfo = typeof(T).GetProperty(value);
        }
    }

    protected override bool GetIsVisible(IRenderContext context)
    {
        if (_propertyInfo == null)
            return false;

        var model = context.Get<T>();

        if (model == null)
            return false;

        return (bool)_propertyInfo.GetValue(model, null);
    }

    protected void SetIsVisibleProperty(Expression<Func<T, bool>> propertyLambda)
    {
        var expression = propertyLambda.Body as MemberExpression;
        if (expression == null)
            throw new ArgumentException("You must pass a lambda of the form: 'vm => vm.Property'");

        PropertyName = expression.Member.Name;
    }
}

public interface ICompanyViewModel
{
    string CompanyName { get; }
    bool IsVisible { get; }
}

public class CompanyControl: HideableControl<ICompanyViewModel>
{
    public CompanyControl()
    {
        SetIsVisibleProperty(vm => vm.IsVisible);
    }
}

Bagian penting bagi saya adalah bahwa di CompanyControlkelas kompiler hanya akan memungkinkan saya untuk memilih properti boolean ICompanyViewModelyang membuatnya lebih mudah bagi pengembang lain untuk memperbaikinya.

Perbedaan utama antara solusi saya dan jawaban yang diterima adalah bahwa kelas saya generik dan saya hanya ingin mencocokkan properti dari tipe generik yang boolean.

bikeman868
sumber
0

Begitulah cara saya mengimplementasikannya, alasan di baliknya adalah jika kelas yang Anda inginkan untuk mendapatkan nama dari anggotanya tidak statis maka Anda perlu membuat instanse untuk itu dan kemudian mendapatkan nama anggota. jadi generik di sini datang untuk membantu

public static string GetName<TClass>(Expression<Func<TClass, object>> exp)
{
    MemberExpression body = exp.Body as MemberExpression;

    if (body == null)
    {
         UnaryExpression ubody = (UnaryExpression)exp.Body;
         body = ubody.Operand as MemberExpression;
    }

     return body.Member.Name;
}

penggunaannya seperti ini

var label = ClassExtension.GetName<SomeClass>(x => x.Label); //x is refering to 'SomeClass'
Mo Hrad A
sumber