(受け入れた回答を使用して作成した以下のソリューションを参照してください)
リフレクションを伴うコードの保守性を改善しようとしています。アプリには、公開されたリモートインターフェイスに含まれていないアプリの一部にアクセスするための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メソッドを簡素化します。ドアノブの研磨は危険な作業です...
みんな、ありがとう。
ここから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#6.0では、これはあなたができるように問題ではなくなりました:
nameof(SomeProperty)
この式は、コンパイル時に"SomeProperty"
に解決されます。
ラムダ式から抽出するための有名なハックがあります(これは、MVVMの基礎でJosh Smithによる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 の完全なコードをご覧になることをお勧めします。
さて、ここに私が作成したものがあります(私が選択した答えと彼が参照した質問に基づいて):
// <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);
PropertyInfo クラスは、私が正しく理解していれば、これを達成するのに役立つはずです。
PropertyInfo[] propInfos = typeof(ReflectedType).GetProperties();
propInfos.ToList().ForEach(p =>
Console.WriteLine(string.Format("Property name: {0}", p.Name));
これはあなたが必要なものですか?
Reflectionを使用して、プロパティの実際の名前を取得できます。
http://www.csharp-examples.net/reflection-property-names/
プロパティに「文字列名」を割り当てる方法が必要な場合、文字列名を取得するために反映できる属性を記述してみませんか?
[StringName("MyStringName")]
private string MyProperty
{
get { ... }
}
ソリューションを変更して、複数のプロパティにチェーンします。
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"
すでに質問にある回答とこの記事に基づいて: https://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic -strings / この問題の解決策を示しています。
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));
}
}
古い質問ですが、この質問に対する別の答えは、CallerMemberNameAttributeを使用するヘルパークラスで静的関数を作成することです。
public static string GetPropertyName([CallerMemberName] String propertyName = null) {
return propertyName;
}
そして、それを次のように使用します:
public string MyProperty {
get { Console.WriteLine("{0} was called", GetPropertyName()); return _myProperty; }
}
それは私がそれを実装した方法です、背後にある理由は、そのメンバーから名前を取得したいクラスが静的でない場合、そのインスタンスを作成してからメンバーの名前を取得する必要があることです。ここでジェネリックは助けになります
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'
StackTraceクラスを使用して、現在の関数の名前を取得できます(または、関数にコードを配置し、レベルを下げて呼び出し元の関数を取得する場合)。
http://msdn.Microsoft.com/en-us/library/system.diagnostics.stacktrace(VS.71).aspx を参照してください
私はこの答えを非常に効果的に使用しています: Expression <Func <TModel、TProperty >>から文字列としてプロパティを取得します
しばらく前にすでにこの質問に答えていたことに気付きました。私の他の答えが持っている唯一の利点は、静的プロパティで機能することです。この答えの構文は、反映したい型の変数を作成する必要がないため、はるかに便利です。
特定のユースケースで既に提案されているソリューションを使用するのは多少困難でしたが、最終的には解決しました。私の特定のケースが新しい質問に値するとは思わないので、参考のためにここにソリューションを投稿しています。 (これは質問と非常に密接に関連しており、私と同様のケースを持つ他の人に解決策を提供します)。
私が終わったコードは次のようになります。
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
のブール型プロパティーのみを選択できるようにすることです。
私のソリューションと受け入れられた答えの主な違いは、私のクラスはジェネリックであり、ジェネリック型のブール型のプロパティのみを一致させたいということです。