Obejście braku operatora "nameof" w C# dla bezpiecznego databindingu typu?
Było wiele sentymentów do włączenia nameof
operatora w C#. Jako przykład działania tego operatora, nameof(Customer.Name)
zwróci łańcuch "Name"
.
Mam obiekt domeny. I muszę to związać. I potrzebuję nazw właściwości jako ciągów. I chcę, żeby były bezpieczne.
Pamiętam, że natknąłem się na obejście w.NET 3.5, które zapewniało funkcjonalność nameof
i obejmowało wyrażenia lambda. Nie udało mi się jednak zlokalizować tego obejście. Czy ktoś może mi to obejść?
Jestem również zainteresowany sposobem zaimplementowania funkcjonalności nameof
w.NET 2.0, jeśli jest to możliwe.
8 answers
Ten kod zasadniczo tak robi:
class Program
{
static void Main()
{
var propName = Nameof<SampleClass>.Property(e => e.Name);
Console.WriteLine(propName);
}
}
public class Nameof<T>
{
public static string Property<TProp>(Expression<Func<T, TProp>> expression)
{
var body = expression.Body as MemberExpression;
if(body == null)
throw new ArgumentException("'expression' should be a member expression");
return body.Member.Name;
}
}
(Oczywiście jest to kod 3.5...)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-19 14:48:07
Podczas gdy Reshefm i Jon Skeet pokazują właściwy sposób, aby to zrobić za pomocą wyrażeń, warto zauważyć, że istnieje tańszy sposób, aby to zrobić dla nazw metod:
Owiń delegata wokół swojej metody, Pobierz MethodInfo i możesz iść. Oto przykład:
private void FuncPoo()
{
}
...
// Get the name of the function
string funcName = new Action(FuncPoo).Method.Name;
Niestety, działa to tylko dla metod; nie działa dla właściwości, ponieważ nie można mieć delegatów do metod getter lub setter właściwości. (Wydaje się głupie ograniczenie, IMO.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-19 15:18:57
Obejściem jest użycie drzewa wyrażeń i rozdzielenie tego drzewa wyrażeń w celu znalezienia odpowiedniego MemberInfo
. Trochę więcej szczegółów i komentarzy jest w tej notce (chociaż nie kod do wyciągnięcia członka - to chyba w innym so question gdzieś).
Niestety, ponieważ drzewa wyrażeń nie istnieją w. NET 2.0, tak naprawdę nie ma ich odpowiedników.
Jednym z rozwiązań, aby uniknąć literówek, jest posiadanie zestawu accesorów, które pobierają odpowiednie PropertyInfo
dla szczególną właściwość, a jednostka je przetestować. To byłoby jedyne miejsce, w którym był sznurek. Uniknęłoby to powielania i ułatwiłoby refaktoryzację, ale jest to trochę drakońskie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-19 12:57:24
Rozszerzenie do tego, co zrobił reshefm, które uprościło użycie operatora nameof () i podaje nazwy metod oraz członków klasy i metod:
/// <summary>
/// Provides the <see cref="nameof"/> extension method that works as a workarounds for a nameof() operator,
/// which should be added to C# sometime in the future.
/// </summary>
public static class NameOfHelper
{
/// <summary>
/// Returns a string represantaion of a property name (or a method name), which is given using a lambda expression.
/// </summary>
/// <typeparam name="T">The type of the <paramref name="obj"/> parameter.</typeparam>
/// <typeparam name="TProp">The type of the property (or the method's return type), which is used in the <paramref name="expression"/> parameter.</typeparam>
/// <param name="obj">An object, that has the property (or method), which its name is returned.</param>
/// <param name="expression">A Lambda expression of this pattern: x => x.Property <BR/>
/// Where the x is the <paramref name="obj"/> and the Property is the property symbol of x.<BR/>
/// (For a method, use: x => x.Method()</param>
/// <returns>A string that has the name of the given property (or method).</returns>
public static string nameof<T, TProp>(this T obj, Expression<Func<T, TProp>> expression)
{
MemberExpression memberExp = expression.Body as MemberExpression;
if (memberExp != null)
return memberExp.Member.Name;
MethodCallExpression methodExp = expression.Body as MethodCallExpression;
if (methodExp != null)
return methodExp.Method.Name;
throw new ArgumentException("'expression' should be a member expression or a method call expression.", "expression");
}
/// <summary>
/// Returns a string represantaion of a property name (or a method name), which is given using a lambda expression.
/// </summary>
/// <typeparam name="TProp">The type of the property (or the method's return type), which is used in the <paramref name="expression"/> parameter.</typeparam>
/// <param name="expression">A Lambda expression of this pattern: () => x.Property <BR/>
/// Where Property is the property symbol of x.<BR/>
/// (For a method, use: () => x.Method()</param>
/// <returns>A string that has the name of the given property (or method).</returns>
public static string nameof<TProp>(Expression<Func<TProp>> expression)
{
MemberExpression memberExp = expression.Body as MemberExpression;
if (memberExp != null)
return memberExp.Member.Name;
MethodCallExpression methodExp = expression.Body as MethodCallExpression;
if (methodExp != null)
return methodExp.Method.Name;
throw new ArgumentException("'expression' should be a member expression or a method call expression.", "expression");
}
}
Aby go użyć:
static class Program
{
static void Main()
{
string strObj = null;
Console.WriteLine(strObj.nameof(x => x.Length)); //gets the name of an object's property.
Console.WriteLine(strObj.nameof(x => x.GetType())); //gets the name of an object's method.
Console.WriteLine(NameOfHelper.nameof(() => string.Empty)); //gets the name of a class' property.
Console.WriteLine(NameOfHelper.nameof(() => string.Copy(""))); //gets the name of a class' method.
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-11 12:00:44
Chyba, że ktoś zmieni zdanie, operator nameof
wygląda na to, że wchodzi w C # 6. Oto uwagi na temat spotkania projektowego:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-28 13:56:13
Jest to część języka w C # 6.0
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-18 21:47:29
Przyjęte rozwiązanie jest ładne, proste i eleganckie.
Jednak Budowanie drzewa wyrażeń jest kosztowne i potrzebuję całej ścieżki własności.
Więc trochę go zmieniłem. Nie jest wcale elegancki, ale jest prosty i działa dobrze w większości przypadków: {]}public static string Property<TProp>(Expression<Func<T, TProp>> expression)
{
var s = expression.Body.ToString();
var p = s.Remove(0, s.IndexOf('.') + 1);
return p;
}
Przykład:
? Nameof<DataGridViewCell>.Property(c => c.Style.BackColor.A);
"Style.BackColor.A"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-19 08:29:02
Odpowiedź z reshefm jest całkiem dobra, ale to jest trochę prostsze API IMO:
Przykład użycia:
NameOf.Property(() => new Order().Status)
using System;
using System.Diagnostics.Contracts;
using System.Linq.Expressions;
namespace AgileDesign.Utilities
{
public static class NameOf
{
///<summary>
/// Returns name of any method expression with any number of parameters either void or with a return value
///</summary>
///<param name = "expression">
/// Any method expression with any number of parameters either void or with a return value
///</param>
///<returns>
/// Name of any method with any number of parameters either void or with a return value
///</returns>
[Pure]
public static string Method(Expression<Action> expression)
{
Contract.Requires<ArgumentNullException>(expression != null);
return ( (MethodCallExpression)expression.Body ).Method.Name;
}
///<summary>
/// Returns name of property, field or parameter expression (of anything but method)
///</summary>
///<param name = "expression">
/// Property, field or parameter expression
///</param>
///<returns>
/// Name of property, field, parameter
///</returns>
[Pure]
public static string Member(Expression<Func<object>> expression)
{
Contract.Requires<ArgumentNullException>(expression != null);
if(expression.Body is UnaryExpression)
{
return ((MemberExpression)((UnaryExpression)expression.Body).Operand).Member.Name;
}
return ((MemberExpression)expression.Body).Member.Name;
}
}
}
Pełny kod jest tutaj: http://agiledesignutilities.codeplex.com/SourceControl/changeset/view/b76cefa4234a#GeneralPurpose/NameOf.cs
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-22 14:37:12