プロパティの名前を文字列として取得します


203

(私が受け入れた答えを使用して作成した以下のソリューションを参照してください)

リフレクションを含むいくつかのコードの保守性を改善しようとしています。アプリには、公開されたリモートインターフェイスに含まれていないアプリの部分にアクセスするためのExecuteというメソッドを公開する.NET Remotingインターフェイスがあります。

ここでは、アプリケーションが実行を介してアクセスできるようになっているプロパティ(この例では静的プロパティ)を指定する方法を示します。

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

したがって、リモートユーザーは次のように呼び出すことができます。

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

アプリはリフレクションを使用してSomeClass.SomePropertyを検索し、その値を文字列として返します。

残念ながら、誰かがSomePropertyの名前を変更して、ExposeProperty()の3番目のパラメーターを変更するのを忘れると、このメカニズムが壊れます。

私は同等のものを必要としています:

SomeClass.SomeProperty.GetTheNameOfThisPropertyAsAString()

ExposePropertyの3番目のパラメーターとして使用するため、リファクタリングツールが名前の変更を処理します。

これを行う方法はありますか?前もって感謝します。

さて、これが私が作成したものです(私が選択した答えと彼が参照した質問に基づいて):

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

使用法:

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

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

この素晴らしい機能を備えた今、ExposePropertyメソッドを単純化する時が来ました。ドアノブの研磨は危険な作業です...

みんな、ありがとう。


9
あなたがあなたのソリューションを追加し、物事を結び付けたことは本当に慣れています。
Simply G.


ソリューションを回答として追加する必要があります。これは、受け入れた回答よりもはるかに簡潔です。
ケニーエビット2016年

1
@ケニー・エビット:完了:)
ジムC

回答:


61

ここからGetMemberInfoを使用する:ラムダ式からプロパティ名を取得するには、次のようにします。

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

それは完全にクールです。どのプロパティタイプでも機能するようです。
ジムC

インスタンスプロパティと静的プロパティの両方で試してみました。ここまでは順調ですね。
ジムC

を含むアセンブリまたはNuGetパッケージを取得できる場所はありGetMemberInfoますか?Microsoft Enterprise Libraryの「一般的なユーティリティ」パッケージでは何も見つかりません。これは、MSDNがそのメソッドを含んでいることを示しているようです。「非公式」パッケージはありますが、非公式であることは刺激的ではありません。これに基づいたJimCの回答ははるかに簡潔であり、利用できないように見えるライブラリに依存していません。
ケニーエビット2016年

1
@KennyEvitt、彼が参照している方法は、彼がリンクした質問の作成者によって書かれたものです。methodyouこのType.GetMembers使用できることに代替msdn.microsoft.com/en-us/library/...
ボン・

464

C#6.0では、これはあなたができるように問題ではなくなりました:

nameof(SomeProperty)

この式はコンパイル時にに解決されます"SomeProperty"

nameofのMSDNドキュメント


18
これは不正であり、ModelState.AddModelError呼び出しに非常に役立ちます。
マイケルシルバー

9
そして、これはconst string!すばらしい
ジャック

4
@RaidenCore確かに、マイクロコントローラー用に作成している場合は、Cなどの低水準言語を使用する必要があります。また、画像やビデオ処理などのパフォーマンスのあらゆるビットを絞る必要がある場合は、CまたはC ++を使用する必要があります。しかし、他の95%のアプリケーションでは、マネージコードフレームワークは十分高速です。最終的にはC#もマシンコードにコンパイルされ、必要に応じてC#をネイティブにプリコンパイルすることもできます。
Tsahi Asher 2017

2
ちなみに、あなたが言及したアプリである@RaidenCoreはC#より前のバージョンなので、C ++で記述されています。彼らが今日書かれたとしたら、誰がどの言語が使われたか知っているでしょう。Paint.NETなどを参照してください。
Tsahi Asher 2017

1
これはRaiseProperty、WPFで使用するときに非常に役立ちます。RaisePropertyChanged( "property")の代わりにRaisePropertyChanged(nameof(property))を使用してください
Pierre

17

ラムダ式から抽出するためのよく知られたハックがあります(これは、ジョシュスミスによるMVVMファウンデーションのPropertyObserverクラスのコードです)。

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

申し訳ありませんが、これにはいくつかのコンテキストがありませんでした。これはTPropertySource、プロパティを含むクラスである、より大きなクラスの一部でした。TPropertySourceで関数をジェネリックにして、クラスから抽出することができます。MVVM Foundationの完全なコードを確認することをお勧めします。


関数の呼び出し方法の例では、これは確かに+1です。おっと、デバッグアサーションに1つあることがわかりませんでした。そのため、開発者を水平方向にスクロールして行の重要な部分に到達するのは悪いことです;)
OregonGhost

うーん...私はそれを理解するためにこれを分析する必要があります。
ジムC

Visual Studio 2008は、「TPropertySource」にエラーのフラグを付けます(「見つかりません」)。
ジムC

私は、C ++のような単なるシンボル<T>ではなく、その型名を認識しました。TPropertySourceは何を表していますか?
ジムC

2
このコンパイルを行うには、あなただけの読み取りにメソッドシグネチャを変更することができpublic static string GetPropertyName<TPropertySource>(Expression<Func<TPropertySource, object>> expression)、その後そのような呼び出し:var name = GetPropertyName<TestClass>(x => x.Foo);
dav_i

16

さて、これが私が作成したものです(私が選択した答えと彼が参照した質問に基づいて):

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

使用法:

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

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

8

PropertyInfoのクラスは、私が正しく理解していれば、あなたは、これを達成するのを助ける必要があります。

  1. Type.GetProperties()メソッド

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

これが必要ですか?


いいえ、アプリが「SomeSecret」のリクエストを受け取るときにGetPropertiesを使用しますが。アプリはマップで「SomeSecret」を検索して、「SomeClass」というクラスの「SomeProperty」というプロパティを見つける必要があることを発見します。
ジムC

nameof(SomeProperty)は、実際にこれを.net 4.0以降から緩和しています。そのような長いハックの必要はありません。
ティワリ

6

Reflectionを使用して、プロパティの実際の名前を取得できます。

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

プロパティに「文字列名」を割り当てる方法が必要な場合は、文字列名を取得するために反映できる属性を作成してみませんか?

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

1
そう、それがアプリが「SomeSecret」の着信リクエストを処理する方法ですが、ExposeProperty問題のツールを提供しません。
ジムC

興味深いことに、MyStringNameを変更しない限り、MyPropertyの名前をハートコンテンツに変更できます。何らかの理由で変更したい場合は、ExposePropertyパラメーターを変更する必要があります。少なくとも、属性の値を変更するには属性を参照する必要があるため(参照場所から実行できるプロパティの名前を変更する場合とは異なります)、属性の横にコメントを追加して、そのような警告を表示できます。
ジムC

6

複数のプロパティをチェーンするようにソリューションを変更しました:

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

使用法:

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

4

すでに質問に含まれている回答とこの記事に基づいています:https : //handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/ Iこの問題に対する私の解決策を提示しています:

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

そして、インスタンスと静的プロパティの使用法も示すテスト:

[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

古い質問ですが、この質問に対する別の答えは、CallerMemberNameAttributeを使用するヘルパークラスに静的関数を作成することです。

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

そしてそれを次のように使用します:

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

0

StackTraceクラスを使用して、現在の関数の名前を取得できます(またはコードを関数に配置した場合は、レベルを下げて呼び出し元の関数を取得します)。

http://msdn.microsoft.com/en-us/library/system.diagnostics.stacktrace(VS.71).aspxを参照してください


スタックトレースをキャプチャする場所がどこにあるかはわかりませんが、プロパティの名前が含まれているとは思いません。
ジムC

これは可能ですが、コンパイラーのインライン最適化が原因で、予期しない結果(例外を含む)になる可能性があります。smelser.net/blog/post/2008/11/27/...
JoeGeeky


0

私の特定のユースケースで既に提案されているソリューションを使用するのは困難でしたが、最終的にはそれを理解しました。私の特定のケースは新しい質問に値するとは思わないので、参考のためにここに私の解決策を投稿します。(これは質問と非常に密接に関連しており、私のケースに似た他の人に解決策を提供します)。

最終的に次のようなコードになります。

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

私にとって重要な部分は、CompanyControlクラスではコンパイラがブール値のプロパティのみを選択できることですICompanyViewModelにすることです。これにより、他の開発者が正しくを取得できるようになります。

私のソリューションと受け入れられた答えの主な違いは、クラスがジェネリックであり、ブールであるジェネリック型のプロパティのみを一致させたいということです。


0

それが私がそれを実装した方法です、その背後にある理由は、そのメンバーから名前を取得したいクラスが静的でない場合、そのインスタンスを作成してからメンバーの名前を取得する必要があるためです。ここで非常に一般的になります

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

使い方はこんな感じ

var label = ClassExtension.GetName<SomeClass>(x => x.Label); //x is refering to 'SomeClass'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.