7

私はいくつかのプロパティを持つクラスを持っています:

class Foo
{
    public int Bar { get; set; }
    public string Baz { get; set; }
    public bool Quux { get; set; }
    (...)
}

一部のストレージ API で使用するには、これらのプロパティのサブセットを文字列として名前で指定する必要があります。

var props = new string[]
{
    "Bar",
    // Don't want this one... "Baz",
    "Quux",
     ...
};

これは機能しますが、安全ではありません。「Quux」と入力を間違えても、コンパイル エラーは発生せず、(うまくいけば) 実行時エラーが発生します。リフレクションを試しtypeof(Foo).GetProperties("Bar")ましたが、それも実行時にのみ失敗します。

理想的には、次のようなことをしたいと思います:

var props = new string[]
{
    Magic_GetName(Foo.Bar),
    // Don't want this one... Foo.Baz,
    Magic_GetName(Foo.Quux),
     ...
};

どうすればそれを達成できますか?

4

3 に答える 3

9

C# 6.0 では、 nameof()キーワードを使用できます。

そして、次のように書きます。

var props = new string[]
{
    nameof(Foo.Bar),
    nameof(Foo.Quux),
     ...
};

このキーワードを使用してコンパイル時にすべてが行われるため、実行時にシンボルの名前を掘り下げるコードでラムダ式を使用するよりもはるかに優れています。パフォーマンスの観点から優れており、次のswitch()ステートメントでも機能します。

switch(e.PropertyName)
{
    case nameof(Foo.Bar):
        break;
}

ラムダ式またはマジック get 関数を使用すると、ステートメントで文字列リテラルを使用する必要がswitch()あるため、ステートメントを使用できません。キーワードはコンパイル時に文字列リテラルに変換されるため、機能しますswitch()nameof()

于 2016-10-12T22:31:25.610 に答える
8

これには式を使用できます。使用法は次のようになります。

Magic_GetName<Foo>(x => x.Bar)

の実装は次のMagic_GetNameようになります。

public static string Magic_GetName<TClass>(
    Expression<Func<TClass, object>> propertyExpression)
{
    propertyExpression.Dump();
    var body = propertyExpression.Body as UnaryExpression;
    if (body == null)
    {
        throw new ArgumentException(
            string.Format(
                CultureInfo.InvariantCulture, 
                "The body of the 'propertyExpression' should be an " +
                "unary expression, but it is a {0}", 
                propertyExpression.Body.GetType()));
    }

    var memberExpression = body.Operand as MemberExpression;
    if (memberExpression == null)
    {
        throw new ArgumentException(
            string.Format(
                CultureInfo.InvariantCulture, 
                "The operand of the body of 'propertyExpression' should " +
                "be a member expression, but it is a {0}", 
                propertyExpression.Body.GetType()));
    }
    var propertyInfo = memberExpression.Member as PropertyInfo;
    if (propertyInfo == null)
    {
        throw new ArgumentException(
            string.Format(
                CultureInfo.InvariantCulture, 
                "The member used in the expression should be a property, " +
                "but it is a {0}", 
                memberExpression.Member.GetType()));
    }

    return propertyInfo.Name;
}

更新: この質問のタイトルは、「コンパイル時にプロパティ名を取得する」です。
私の答えは実際にはそうではありません。メソッドMagic_GetNameは実行時に実行されるため、パフォーマンスに影響があります。

一方、CallerMemberName属性を使用する .NET 4.5 の方法は、実際にはコンパイル時の機能であり、ランタイムへの影響はありません。ただし、コメントで既に述べたように、特定のシナリオには適用できません。

于 2013-04-10T07:48:33.177 に答える
4

それを行うためのはるかに良い方法は

GetPropertyName<MemoryDevice>(x => x.DeviceLocator)

public static string GetPropertyName<TClass>(
        Expression<Func<TClass,object>> propertyExpression)
    {
        var body = propertyExpression.ToString();
        body = body.Substring(body.IndexOf(".")+1);
        return body;
    }

実行時にそれを行う別の方法は

public static string GetName<TClass>(
    Expression<Func<TClass, object>> propertyExpression)
{
    var body = propertyExpression.Body as UnaryExpression;
    var memberExpression = body.Operand as MemberExpression;
    var propertyInfo = memberExpression.Member as PropertyInfo;

    return propertyInfo.Name;
}
于 2013-07-11T13:23:45.460 に答える