Почему 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

Взгляните на этот метод, взятый из:

https://github.com/okhosting/OKHOSTING.Data/blob/master/src/PCL/OKHOSTING.Data/Validation/MemberExpression.cs

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;
}
Другие вопросы по тегам