Özelliğin adını dize olarak alma


204

(Kabul ettiğim yanıtı kullanarak oluşturduğum aşağıdaki çözüme bakın)

Yansıma içeren bazı kodların sürdürülebilirliğini artırmak için çalışıyorum. Uygulama, yayınlanan uzak arabiriminde bulunmayan uygulama bölümlerine erişmek için Execute adlı bir yöntemi (diğer şeylerin yanı sıra) ortaya çıkaran bir .NET Remoting arabirimine sahiptir.

Uygulamanın Execute aracılığıyla erişilebilir olması gereken özellikleri (bu örnekte statik olan) nasıl belirlediği aşağıda açıklanmıştır:

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

Böylece uzaktaki bir kullanıcı arayabilir:

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

ve uygulama SomeClass.SomeProperty'yi bulmak ve değerini bir dize olarak döndürmek için yansımayı kullanır.

Ne yazık ki, birisi SomeProperty olarak yeniden adlandırır ve ExposeProperty () öğesinin 3. parm değerini değiştirmeyi unutursa, bu mekanizmayı bozar.

Ben eşdeğer gerekir:

SomeClass.SomeProperty.GetTheNameOfThisPropertyAsAString()

ExposeProperty'de 3. parm olarak kullanmak için yeniden düzenleme araçları yeniden adlandırmalarla ilgilenir.

Bunu yapmanın bir yolu var mı? Şimdiden teşekkürler.

Tamam, oluşturduğum şey (seçtiğim cevaba ve referans aldığı soruya dayanarak):

// <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;
 }

Kullanımı:

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

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

Şimdi bu harika özellik ile, ExposeProperty yöntemini basitleştirmenin zamanı geldi. Kapı tokmağını parlatmak tehlikeli bir iştir ...

Herkese teşekkürler.


9
Çözümünüzü eklediğiniz ve işleri bağladığınız gerçekten appriciated.
Sadece G.


Çözümünüzü bir cevap olarak eklemelisiniz - kabul ettiğiniz cevaptan çok daha özlüdür.
Kenny Evitt

1
@Kenny Evitt: Bitti:)
Jim C

@JimC Olumlu oy! Ve şu anda kabul edilen cevap üzerine bir yorumda bağlantılı . Teşekkürler!
Kenny Evitt

Yanıtlar:


61

Buradan GetMemberInfo'yu kullanarak: lambda ifadesinden Özellik adını almak gibi bir şey yapabilirsiniz:

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);
    }
}

Tamamen harika. Görünüşe göre herhangi bir özellik türü üzerinde de çalışır.
Jim C

Sadece hem örnek hem de statik özellikleri ile denedim. Çok uzak çok iyi.
Jim C

İçerdiği montaj veya NuGet paketini nereden bulabilirim GetMemberInfo? MSDN'nin bu yöntemi içerdiğini belirten Microsoft Enterprise Library için 'ortak yardımcı programlar' paketiyle hiçbir şey bulamıyorum. Bir "gayri resmi" paket var ama gayri resmi olmak sönük. JimC'nin buna dayanan cevabı çok daha özlü ve görünüşte kullanılamayan bir kütüphaneye dayanmıyor.
Kenny Evitt

1
@KennyEvitt, referansta bulunduğu yöntem, bağlantılandırdığı sorunun yazarının yazdığı yöntemdir. Bu metodyou alternatif olarak bu Type.GetMembers msdn.microsoft.com/en-us/library/…
Bon

465

C # 6.0 ile, bu artık yapabileceğiniz gibi bir sorun değil:

nameof(SomeProperty)

Bu ifade derleme zamanında şu şekilde çözülür: "SomeProperty" .

MSDN ad belgesi .


18
Bu badass ve ModelState.AddModelError çağrıları için çok kullanışlıdır.
Michael Silver

9
Ve bu bir const string! Şaşırtıcı
Jack

4
Bir mikrodenetleyici için yazıyorsanız, C gibi düşük düzeyli bir dil kullanmanız ve görüntü ve video işleme gibi her performans bitini sıkmanız gerekiyorsa, C veya C ++ kullanmanız gerekir. ancak uygulamaların diğer% 95'i için yönetilen bir kod çerçevesi yeterince hızlı olacaktır. Sonunda C # da makine koduna derlenir ve hatta isterseniz yerel olarak önceden derleyebilirsiniz.
Tsahi Asher

2
Bu arada, bahsettiğiniz uygulamalar @ RaidenCore, C #'dan önce gelir, bu yüzden C ++ ile yazılırlar. Bugün yazıldıysa, hangi dilin kullanıldığını kim bilebilir? Bkz. Örneğin Paint.NET.
Tsahi Asher

1
WPF'de istediğinizde bu gerçekten kullanışlıdır RaiseProperty! RaisePropertyChanged ("property") yerine RaisePropertyChanged (nameof (property)) kullanın
Pierre

17

Lambda ifadesinden ayıklamak için iyi bilinen bir kesmek var (bu, MVVM temelindeki Josh Smith tarafından PropertyObserver sınıfından):

    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;
    }

Maalesef, bunun bir bağlamı eksikti. Bu, TPropertySourceözelliği içeren sınıfın bulunduğu daha büyük bir sınıfın parçasıydı . Sınıftan ayıklamak için işlevi TPropertySource içinde genel yapabilirsiniz. MVVM Foundation'dan tam koda bir göz atmanızı öneririm .


İşlevin nasıl çağrılacağına dair bir örnekle, bu kesinlikle +1. Hata! Hata ayıklama iddiasında bir tane olduğunu görmedim - bu yüzden bir geliştiricinin bir satırın önemli kısmına ulaşmak için yatay kaydırma yapması kötüdür;)
OregonGhost

Hmmm ... Bunu anlamak için bunu kesmem gerekiyor.
Jim C

Visual Studio 2008 "TPropertySource" u hata olarak işaretler ("bulunamıyor").
Jim C

Ben sadece bir tür adı sadece bir simge <T> C ++ gibi fark ettim. TPropertySource neyi temsil eder?
Jim C

2
Bu derlemeyi yapmak için, sadece okumak için yöntem imzasını değiştirebilir ve public static string GetPropertyName<TPropertySource>(Expression<Func<TPropertySource, object>> expression)şu şekilde arayabilirsiniz:var name = GetPropertyName<TestClass>(x => x.Foo);
dav_i

16

Tamam, oluşturduğum şey (seçtiğim cevaba ve referans aldığı soruya dayanarak):

// <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;
 }

Kullanımı:

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

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

8

PropertyInfo sınıf doğru anlamak eğer, bunu başarmak yardımcı olmalıdır.

  1. Type.GetProperties () yöntemi

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

Senin ihtiyacın olan şey bu mu?


Hayır, app "SomeSecret" için istek aldığında GetProperties kullanmak rağmen. Uygulama, "SomeClass" adlı bir sınıfta "SomeProperty" adlı bir özellik bulması gerektiğini keşfetmek için bir haritada "SomeSecret" ifadesini arar.
Jim C

nameof (SomeProperty) bunu .net 4.0'dan itibaren kolaylaştırır. Bu kadar uzun saldırılara gerek yok.
Div Tiwari

6

Özelliklerin gerçek adlarını elde etmek için Yansıma özelliğini kullanabilirsiniz.

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

Bir özelliğe "Dize Adı" atamanın bir yoluna ihtiyacınız varsa, dize adını almak için neden üzerinde düşünebileceğiniz bir özellik yazmıyorsunuz?

[StringName("MyStringName")]
private string MyProperty
{
    get { ... }
}

1
Ya, uygulama "SomeSecret" için gelen istekleri bu şekilde işler, ancak bana ExposeProperty sorunu için bir araç vermez.
Jim C

İlginç ... daha sonra MyStringName ile uğraşmadığınız sürece MyProperty'i kalplerinizin içeriğine yeniden adlandırabilirsiniz ve eğer herhangi bir nedenle değiştirmek istiyorsanız ExposeProperty parmını değiştirmeniz gerekir. En azından, herhangi bir referans konumundan yapılabilecek bir özelliği yeniden adlandırmanın aksine, özniteliğin değerini değiştirmek için ona bakmanız gerektiğinden, böyle bir uyarıyla özniteliğin yanına bir yorum ekleyebilirsiniz.
Jim C

6

Çözümünüzü birden fazla mülk üzerinde zincir oluşturacak şekilde değiştirdim:

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;
}

Kullanımı:

string name = GetPropertyName(() => someObject.SomeProperty.SomeOtherProperty);
// returns "SomeProperty.SomeOtherProperty"

4

Zaten soruda yer alan cevaba ve bu makaleye dayanarak: https://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/ I bu soruna çözümümü sunuyorum:

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;
    }
}

Örnek ve statik özelliklerin kullanımını da gösteren bir Test:

[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));
    }
}

3

Eski soru, ancak bu sorunun başka bir yanıtı CallerMemberNameAttribute kullanan bir yardımcı sınıfta statik bir işlev oluşturmaktır.

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

Ve sonra şöyle kullanın:

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

0

Geçerli işlevin adını almak için StackTrace sınıfını kullanabilirsiniz (veya kodu bir işleve koyarsanız, bir düzey aşağı inip arama işlevini alabilirsiniz).

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


Yığın izleme yakalamak için aklınızda nerede olduğunu bilmiyorum, ama mülkiyet adını içeren birini düşünemiyorum.
Jim C

Bunu yapabilirsiniz, ancak bu, derleyici satır içi optimizasyonları nedeniyle beklenmedik sonuçlara (istisnalar dahil) yol açabilir. smelser.net/blog/post/2008/11/27/…
JoeGeeky


0

Özel kullanım durumum için önerilen çözümleri kullanmakta zorlandım, ancak sonunda anladım. Özel durumumun yeni bir soruya layık olduğunu düşünmüyorum, bu yüzden çözümümü buraya referans olarak gönderiyorum. (Bu soru ile çok yakından ilgilidir ve benimkine benzer bir vakası olan herkes için bir çözüm sağlar).

Ben sona erdi kodu şöyle görünüyor:

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);
    }
}

Benim için önemli olan CompanyControl, derleyicinin derste sadece bir boolean özelliği seçmeme izin vermesidir.ICompanyViewModel bu da diğer geliştiricilerin doğru yapmasını kolaylaştırıyor.

Benim çözüm ve kabul edilen cevap arasındaki ana fark benim sınıf genel ve sadece boolean genel tür özellikleri eşleştirmek istiyorum.


0

Ben bunu nasıl uyguladım, arkasındaki nedeni üyeden adını almak istediğiniz sınıf statik değilse o zaman bunun bir instanse oluşturmak ve sonra üyenin adını almak gerekir. bu yüzden jenerik burada yardıma geliyor

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;
}

kullanım böyle

var label = ClassExtension.GetName<SomeClass>(x => x.Label); //x is refering to 'SomeClass'
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.