Обход проблемы из-за отсутствия оператора 'nameof' в С# для безопасного хранения данных по типу?
Было много настроений, чтобы включить оператор nameof
в С#. В качестве примера того, как будет работать этот оператор, nameof(Customer.Name)
вернет строку "Name"
.
У меня есть объект домена. И я должен связать это. И тогда мне нужны имена свойств. И я хочу, чтобы они были безопасными по типу.
Я помню, что встречал обходной путь в .NET 3.5, который обеспечивал функциональность nameof
и включал лямбда-выражения. Однако я не смог найти это решение. Может ли кто-нибудь предоставить это обходное решение для меня?
Мне также интересен способ реализовать функциональность nameof
в .NET 2.0, если это возможно.
Ответы
Ответ 1
Этот код в основном делает это:
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;
}
}
(Конечно, это код 3.5)
Ответ 2
В то время как reshefm и Jon Skeet показывают правильный способ сделать это с помощью выражений, стоит отметить более дешевый способ сделать это для имен методов:
Оберните делегат вокруг своего метода, получите MethodInfo, и вам хорошо идти. Вот пример:
private void FuncPoo()
{
}
...
// Get the name of the function
string funcName = new Action(FuncPoo).Method.Name;
К сожалению, это работает только для методов; он не работает для свойств, так как вы не можете делегировать функции getter или setter. (Похоже, это глупое ограничение, ИМО.)
Ответ 3
Обходным путем является использование дерева выражений и разделение этого дерева выражений на части, чтобы найти соответствующий MemberInfo
. Немного больше деталей и комментариев в эта заметка (хотя и не код для вытягивания члена - что в другом вопросе SO где-то, я считаю).
К сожалению, поскольку деревья выражений не существуют в .NET 2.0, на самом деле нет эквивалента.
Одним из решений, чтобы избежать опечаток, является наличие набора аксессуаров, которые извлекают соответствующий PropertyInfo
для определенного свойства и unit test их. Это было бы единственное место, в котором была строка. Это позволит избежать дублирования и упростить рефакторинг, но это немного драконов.
Ответ 4
Расширение того, что сделал решеф, что упростило использование оператора nameof(), а также дает имена методов и членов класса и методов:
/// <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 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 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");
}
}
Чтобы использовать его:
static class Program
{
static void Main()
{
string strObj = null;
Console.WriteLine(strObj.nameof(x => x.Length)); //gets the name of an object property.
Console.WriteLine(strObj.nameof(x => x.GetType())); //gets the name of an object 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.
}
}
Ответ 5
Если кто-то не передумает, оператор nameof
выглядит так, как будто он входит в С# 6. Вот заметки о дизайне:
https://roslyn.codeplex.com/discussions/552376
https://roslyn.codeplex.com/discussions/552377
Ответ 6
Принятое решение приятное, простое и элегантное.
Однако создание дерева выражений является дорогостоящим, и мне нужен весь путь свойства.
Поэтому я немного изменил его. Он не является элегантным вообще, но он прост и хорошо работает в большинстве случаев:
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;
}
Пример:
? Nameof<DataGridViewCell>.Property(c => c.Style.BackColor.A);
"Style.BackColor.A"
Ответ 7
Ответ от reshefm довольно хорош, но это немного проще API IMO:
Пример использования:
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;
}
}
}
Полный код находится здесь:
http://agiledesignutilities.codeplex.com/SourceControl/changeset/view/b76cefa4234a#GeneralPurpose/NameOf.cs
Ответ 8
Это часть языка в С# 6.0
https://msdn.microsoft.com/en-us/magazine/dn802602.aspx