Почему nameof возвращает только фамилию?
nameof(order.User.Age) возвращает только "Age" вместо "order.User.Age"
Какова причина сделать это более ограниченным способом? Если мы хотим только фамилию, мы могли бы сделать что-то вроде
public static GetLastName(this string x) {
return string.Split(x, '.').Last();
}
nameof(order.User.Age).GetLastName()
И с одним оператором мы можем получить и "Age", и "order.User.Age". Но с текущей реализацией мы можем получить только "Возраст".
Есть ли какая-то логика за этим решением?
Изменить: например, такое поведение необходимо для привязки MVC
Html.TextBox(nameof(order.User.Age))
6 ответов
Обратите внимание, что если вам нужно / нужно "полное" имя, вы можете сделать это:
$"{nameof(order)}.{nameof(User)}.{nameof(Age)}".GetLastName();
пока все эти имена находятся в текущей области видимости.
Очевидно, что в этом случае это не очень полезно (имена не будут находиться в области видимости при вызове Razor), но это может быть, если вам нужно, например, полное имя, определенное в пространстве имен, типа для вызова Type.GetType()
или что-то.
Если имена не находятся в области видимости, вы все равно можете сделать несколько более неуклюже:
$"{nameof(order)}.{nameof(order.User)}.{nameof(order.User.Age)}".GetLastName();
- хотя шансы, по крайней мере, один из них должен быть в объеме (если User.Age
является статическим свойством).
У меня была та же проблема, и я реализовал класс, который действует как замена nameof()
ключевое слово, чтобы получить полное имя поставляемого выражения. Он очень вдохновлен ответом OK HOSTING. Все запечено и готово к использованию:
public static class NameOf<TSource>
{
#region Public Methods
public static string Full(Expression<Func<TSource, object>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
{
var unaryExpression = expression.Body as UnaryExpression;
if (unaryExpression != null && unaryExpression.NodeType == ExpressionType.Convert)
memberExpression = unaryExpression.Operand as MemberExpression;
}
var result = memberExpression.ToString();
result = result.Substring(result.IndexOf('.') + 1);
return result;
}
public static string Full(string sourceFieldName, Expression<Func<TSource, object>> expression)
{
var result = Full(expression);
result = string.IsNullOrEmpty(sourceFieldName) ? result : sourceFieldName + "." + result;
return result;
}
#endregion
}
Использование его в вашем коде будет выглядеть так:
class SpeciesFamily
{
public string Name { get; set; }
}
class Species
{
public SpeciesFamily Family { get; set; }
public string Name { get; set; }
}
class Cat
{
public Species Species { get; set; }
}
// Will return a string containing "Species.Family.Name".
var fullName = NameOf<Cat>.Full(c => c.Species.Family.Name);
// Will return a string containing "cat.Species.Name".
var fullNameWithPrefix = NameOf<Cat>.Full("cat", c => c.Species.Name);
Потому что это именно то, что было изобретено. Как вы можете прочитать в уже связанных дискуссиях, здесь вы используете nameof
оператор как nameof(member-access)
, в форме E.I<A1…AK>
, который вернет:
Все эти случаи решаются с использованием правил простого поиска имен $7.6.2 или доступа к членам $7.6.4. Если они преуспевают в связывании, они должны связываться с одним из:
- Метод-группа. Это приводит к ошибке "Чтобы указать имя метода, вы должны предоставить его аргументы".
- Переменная, значение, параметр, константа, член перечисления, доступ к свойству, поле, событие, параметр типа, пространство имен или тип. В этом случае результатом оператора nameof является просто "I", которое обычно является именем символа, с которым связан аргумент. Есть несколько предостережений...
Так что в этом случае он, по определению, должен оценивать все выражения перед всеми точками, шаг за шагом, и после этого оценивать последнее, чтобы получить его Name
:
order.User.Age --> User.Age --> Age
Взгляните на этот метод, взятый из:
public static string GetMemberString(System.Linq.Expressions.Expression<Func<T, object>> member)
{
if (member == null)
{
throw new ArgumentNullException("member");
}
var propertyRefExpr = member.Body;
var memberExpr = propertyRefExpr as System.Linq.Expressions.MemberExpression;
if (memberExpr == null)
{
var unaryExpr = propertyRefExpr as System.Linq.Expressions.UnaryExpression;
if (unaryExpr != null && unaryExpr.NodeType == System.Linq.Expressions.ExpressionType.Convert)
{
memberExpr = unaryExpr.Operand as System.Linq.Expressions.MemberExpression;
if(memberExpr != null)
{
return memberExpr.Member.Name;
}
}
}
else
{
//gets something line "m.Field1.Field2.Field3", from here we just remove the prefix "m."
string body = member.Body.ToString();
return body.Substring(body.IndexOf('.') + 1);
}
throw new ArgumentException("No property reference expression was found.", "member");
}
Некоторые из важных целей использования nameof
чтобы получить последнее "имя" в выражении.
Например имя параметра при броске ArgumentNullException
:
void Method(string parameter)
{
if (parameter == null) throw new ArgumentNullException(nameof(parameter));
}
MVC Action ссылки
<%= Html.ActionLink("Sign up",
@typeof(UserController),
@nameof(UserController.SignUp))
%>
INotifyPropertyChanged
int p {
get { return this._p; }
set { this._p = value; PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.p)); }
}
Дополнительная информация: https://roslyn.codeplex.com/discussions/570551
В .NET 6 добавлено CallerArgumentExpression , которое позволяет создать обходную версиюfullnameof
.
using System.Runtime.CompilerServices;
var fullString = StringOf(nameof(HttpResponseMessage.Content.Headers));
// Prints: HttpResponseMessage.Content.Headers
Console.WriteLine(fullString);
static string StringOf(string value, [CallerArgumentExpression(nameof(value))] string fullpath = default!)
{
// value is: "value"
// fullpath is: "nameof(HttpResponseMessage.Content.Headers)"
// Do some validation here...
// Strip "nameof(", ... ")"
string outputString = fullpath.Substring(fullpath.IndexOf("(") + 1, fullpath.IndexOf(")") - fullpath.IndexOf("(") - 1);
return outputString;
}