Почему nameof возвращает только фамилию?

#c#

#.net #c #-6.0 #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 . Но с текущей реализацией мы можем получить только Age . Есть ли какая-то логика в этом решении? Например, такое поведение необходимо для привязки MVC

 Html.TextBox(nameof(order.User.Age))
  

Комментарии:

1. Обратите order.User внимание, что также может быть некоторой функцией, возвращающей объект со свойством Name . На что должен ссылаться nameof в таких случаях?

2. nameof это постоянная времени компиляции, так как же она будет работать на экземпляре?

3. Чтобы узнать почему, вам придется пройти через долгие обсуждения на roslyn.codeplex.com

4. @leppie для этого есть три варианта использования, как видно из обсуждений Roslyn.

5. Почему это помечено как версия c #-6.0, .net-5.0. 5 или 6, о которой мы говорим?

Ответ №1:

Обратите внимание, что если вам нужно / нужно «полное» имя, вы могли бы сделать это:

 $"{nameof(order)}.{nameof(User)}.{nameof(Age)}".GetLastName();
  

пока все эти имена находятся в текущей области.

Очевидно, что в данном случае это не совсем так полезно (имена не будут находиться в области видимости при вызове Razor), но это может быть, если вам нужно, например, полное имя пространства имен определенного типа для вызова Type.GetType() или что-то в этом роде.

Если имена не входят в область видимости, вы все равно можете сделать несколько более неуклюжий:

 $"{nameof(order)}.{nameof(order.User)}.{nameof(order.User.Age)}".GetLastName();
  

— хотя есть вероятность, что хотя бы один из них должен быть в области видимости (если User.Age это не статическое свойство).

Комментарии:

1. Это не работает, так nameof как должно быть полностью указано. Таким образом, это выглядело бы так $"{nameof(order)}.{nameof(order.User)}.{nameof(order.User.Age)}" , точно указывая точку операции.

2. Это абсолютно работает — nameof не нужно указывать полностью, параметры для него просто должны быть в области видимости. Возможно, я предполагал, что все эти имена были в области видимости, но это не значит, что это не работает. Очевидно, что если вы пытаетесь использовать его в другой области, они должны быть квалифицированы, но я не понимаю, как это действительно влияет на ответ.

3. Возможно, я неправильно сформулировал это, указав «полностью квалифицированный». Однако это влияет на ответ, поскольку, судя по моему прочтению OP, они не все находятся в области видимости.

4. как это $"{nameof(order)}.{nameof(User)}.{nameof(Age)}".GetLastName(); имеет смысл? Для того, чтобы эти свойства были «в области видимости» order , должны быть this и User и Age должны быть свойствами, для this которых они не являются:/ Это не отвечает на вопрос op. И что, черт возьми, находится GetLastName() в строке?

5. Очень маловероятно, что вы бы так не сказали.

Ответ №2:

Потому что это именно то, для чего это было изобретено. Как вы можете прочитать в уже связанных обсуждениях, здесь вы используете nameof оператор as nameof(member-access) формы E.I<A1…AK> , который вернет:

Все эти случаи разрешаются с использованием правил простого поиска имен $ 7.6.2 или доступа к участникам $ 7.6.4. Если им удается выполнить привязку, они должны привязаться к одному из:

  • Метод-группа. Это приводит к ошибке «Чтобы указать имя метода, вы должны предоставить его аргументы».
  • Переменная, значение, параметр, константа, элемент перечисления, доступ к свойству, поле, событие, параметр типа, пространство имен или тип. В этом случае результатом оператора nameof является просто «I», который обычно является именем символа, с которым связан аргумент. Есть несколько предостережений…

Итак, в этом случае он, по его определению, должен вычислять все выражения перед всеми точками, шаг за шагом, и после этого вычислять последнее, чтобы получить его Name :

 order.User.Age --> User.Age --> Age
  

Ответ №3:

У меня была та же проблема, и я реализовал класс, который действует как замена 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 amp;amp; unaryExpression.NodeType == ExpressionType.Convert)
                memberExpression = unaryExpression.Operand as MemberExpression;
        }

        var result = memberExpression.ToString();
        result = result.Substring(result.IndexOf('.')   1);

        return resu<
    }

    public static string Full(string sourceFieldName, Expression<Func<TSource, object>> expression)
    {
        var result = Full(expression);
        result = string.IsNullOrEmpty(sourceFieldName) ? result : sourceFieldName   "."   resu<
        return resu<
    }

    #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);
  

Комментарии:

1. Если вы передаете массив в выражение. Например, возьмите это выражение phone => phone.Lines[1].AsteriskSipPeer , которое оно вернет Lines.get_Item(1).AsteriskSipPeer . Я просто добавил это регулярное выражение, чтобы позаботиться об этом result = Regex.Replace(result, @"(?x) get_Item ( (d ) )", m=> $"[{m.Groups[1].Value}]")

Ответ №4:

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

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 amp;amp; 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");
    }
  

Ответ №5:

Некоторые из важных целей использования nameof — получить последнее «имя» в выражении.

Например, параметр nameof при выбрасывании ArgumentNullException :

 void Method(string parameter)
{
     if (parameter == null) throw new ArgumentNullException(nameof(parameter));
}
  

Ссылки на действия MVC

 <%= 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

Комментарии:

1. Как насчет привязки MVC к Html.TextBox(nameof(User.Name ))?

2. Ммм, «все цели»? Нет, я бы сказал обратное. Например, когда вы сталкиваетесь с неожиданной нулевой ссылкой и хотите зарегистрировать ошибку, но не хотите создавать исключение, вы хотели бы предоставить как можно больше контекста относительно того, что именно было null . Возьмем этот пример: Экземпляр. Окно. textLabel . Текстовое поле. StringLabel . Значение. Ссылка. Объект. Предположим, что «Объект» равен нулю. Что было бы более полезно для диагностики?: просто «Объект» или «Экземпляр. Окно. textLabel . Текстовое поле. StringLabel . Значение. Ссылка. Объект»?

3. Я бы сказал, что null — это что-то совсем другое, но если вы посмотрите на случаи с хлебом и маслом, перечисленные в ссылке, ведение журнала также присутствует, исходя из этого, ваш случай, вероятно, будет зарегистрирован следующим образом: MyMethod(InsaneTypeThatIsNull myVar) { Log(nameof(MyMethod), $"{nameof(myVar)} was null"); } легко отследить это и найти тип

4. @ais для привязки к MVC вы должны написать шаблон редактирования для своей пользовательской модели, а затем использовать @Html.EditorFor(x => x.User)