Ihr erstes Muster, das ich denke, das Beste ist, Sie so weit als Lesbarkeit
geht Ich glaube nicht, dass Sie auf, dass unter Berücksichtigung C# Regeln im Zusammenhang mit Lambda-Ausdrücke verbessern. Das heißt, ich denke, dass einige Verbesserungen vorgenommen werden können.
Erweitern Sie zuerst die Funktionalität auf andere Lambda-Ausdruckstypen. Sie würden mehr als Func<T>
Typen benötigen, um alle Fälle zu behandeln. Entscheiden Sie, welche Ausdruckstypen Sie behandeln müssen. Zum Beispiel, wenn Sie eine Func<S, T>
Art haben (wie in Ihrer Frage - Bar
auf Foo
), sieht es besser aus.Vergleichen Sie diese
myclass.GetMemberName(() => new Foo().Bar);
mit
myclass.GetMemberName<Foo>(x => x.Bar);
ich diese Überlastungen sagen würde tun würde:
//for static methods which return void
public static string GetMemberName(Expression<Action> expr);
//for static methods which return non-void and properties and fields
public static string GetMemberName<T>(Expression<Func<T>> expr);
//for instance methods which return void
public static string GetMemberName<T>(Expression<Action<T>> expr);
//for instance methods which return non-void and properties and fields
public static string GetMemberName<S, T>(Expression<Func<S, T>> expr);
Jetzt können diese verwendet werden, nicht nur in den in den Kommentaren genannten Fällen sicher gibt es überlappende Szenarien. Zum Beispiel, wenn Sie bereits eine Instanz von Foo
haben, dann ist es einfacher, die zweite Überladung (Func<T>
) Überladung für den Namen der Eigenschaft Bar
, wie myclass.GetMemberName(() => foo.Bar)
.
Zweitens implementieren Sie eine GetMemberName
Funktionalität für alle diese Überlastungen. Eine Erweiterungsmethode unter Expression<T>
oder LambdaExpression
würde ausreichen. Ich bevorzuge Letzteres, so dass Sie es sogar in nicht stark typisierten Szenarien nennen können. Ich würde es so schreiben, from this answer:
public static string GetMemberName(this LambdaExpression memberSelector)
{
Func<Expression, string> nameSelector = null;
nameSelector = e => //or move the entire thing to a separate recursive method
{
switch (e.NodeType)
{
case ExpressionType.Parameter:
return ((ParameterExpression)e).Name;
case ExpressionType.MemberAccess:
return ((MemberExpression)e).Member.Name;
case ExpressionType.Call:
return ((MethodCallExpression)e).Method.Name;
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
return nameSelector(((UnaryExpression)e).Operand);
case ExpressionType.Invoke:
return nameSelector(((InvocationExpression)e).Expression);
case ExpressionType.ArrayLength:
return "Length";
default:
throw new Exception("not a proper member selector");
}
};
return nameSelector(memberSelector.Body);
}
Schließlich GetMemberName
ist kein guter Name, wenn Sie es nennen Nichtverlängerung Weg sind. expression.GetMemberName()
klingt logischer. Member.NameFrom<int>(x => x.ToString())
oder MemberName.From<string>(x => x.Length)
usw. sind aussagekräftigere Namen für statische Aufrufe.
Also insgesamt könnte die Klasse wie folgt aussehen:
public static class Member
{
public static string NameFrom(Expression<Action> expr)
{
return expr.GetMemberName();
}
public static string NameFrom<T>(Expression<Func<T>> expr)
{
return expr.GetMemberName();
}
public static string NameFrom<T>(Expression<Action<T>> expr)
{
return expr.GetMemberName();
}
public static string NameFrom<T>(Expression<Func<T, object>> expr)
{
return expr.GetMemberName();
}
}
Und Nutzung:
var name1 = Member.NameFrom(() => Console.WriteLine());
var name2 = Member.NameFrom(() => Environment.ExitCode);
var name3 = Member.NameFrom<Control>(x => x.Invoke(null));
var name4 = Member.NameFrom<string>(x => x.Length);
prägnanteste und sauber.
Arbeitete für mich .. –
Ich bin mir nicht sicher, ich folge Ihrem Kommentar, es funktioniert für Sie, ohne zuerst auf den spezifischen Ausdruck> 'oder diese Art der Berufung ist zufriedenstellend? –
mlorbetske
Ihre Antwort funktionierte für mich '((Ausdruck>) (() => neu Foo(). Bar)). GetMemberName()' –