이름의 목적은 무엇입니까?


263

버전 6.0에는의 새로운 기능이 nameof있지만 변수 이름 만 사용하고 컴파일시 문자열로 변경하기 때문에 그 목적을 이해할 수 없습니다.

나는 그것을 사용할 때 어떤 목적이있을 것이라고 생각 <T>했지만 그것을 시도 할 때 사용 된 유형 대신 nameof(T)나를 인쇄 T합니다.

목적에 대한 아이디어가 있습니까?



28
T전에 그것을 얻을 수있는 방법이 없었습니다 . 이전에 사용 된 유형을 얻는 방법이있었습니다.
존 한나

처음에는 과잉 인 것처럼 보였지만 여전히 그것을 사용해야 할 강력한 이유가 보이지 않습니다. 아마도 MVC 예일까요?
Corey Alix

15
내의 이름을 다시 만들거나 이름을 바꿀 때 확실히 유용합니다 nameof. 오타 예방에도 도움이됩니다.
bvj

4
nameof 의 공식 문서는 여기로 이동되었습니다. docs.microsoft.com/en-us/dotnet/csharp/language-reference/…- 또한 질문에 대한 적절한 답변으로 사용되는 주요 사용 사례도 나열합니다.
markus s

답변:


323

예를 들어 속성 ​​이름을 기준으로 예외를 발생 시키거나 PropertyChanged이벤트를 처리 할 때와 같이 속성 이름을 재사용하려는 경우는 어떻습니까? 재산의 이름을 원할 경우가 많이 있습니다.

이 예제를 보자 :

switch (e.PropertyName)
{
    case nameof(SomeProperty):
    { break; }

    // opposed to
    case "SomeOtherProperty":
    { break; }
}

첫 번째 경우 이름을 바꾸면 SomeProperty속성 이름 도 변경되거나 컴파일이 중단됩니다. 마지막 경우는 그렇지 않습니다.

이는 코드를 컴파일하고 버그가없는 상태로 유지하는 데 매우 유용한 방법입니다.

( Eric Lippert아주 좋은 기사는infoof그것을하지 nameof않았습니까?)


1
VS가 비슷한 기능을 가지고 있는지 확실하지 않은 경우 이름을 리팩터링 할 때 resharper가 문자열을 변경한다는 점을 이해합니다.
Ash Burlaczenko

7
그렇습니다. 그러나 Resharper와 VS는 예를 들어 프로젝트에서 작동하지 않습니다. 그렇습니다. 실제로 이것은 더 나은 솔루션입니다.
Patrick Hofman

49
또 다른 일반적인 사용 사례는 nameof하드 코딩 된 문자열 대신 및 작업 이름을 사용하여 MVC에서 라우팅 하는 것입니다.
RJ Cuthbertson

2
@sotn 나는 당신이 무엇을 요구하는지 이해하지 못합니다. 당신이 그것을 사용하는 것을 막을 것은 아무것도 없습니다 public class MyController { public ActionResult Index() { return View(nameof(Index)); } }-당신은 nameof비 정적 멤버에서 사용할 수 있습니다 (예를 들어 nameof(MyController.Index)위의 클래스를 사용하여 호출 할 수 있으며 "인덱스"를 방출합니다). 에서 예제를 확인 msdn.microsoft.com/en-us/library/...
RJ Cuthbertson에게

2
왜 특별한 지 모르겠습니다. 변수 이름은 항상 동일합니까? 인스턴스가 있든 없든 변수 이름은 @sotn으로 변경되지 않습니다.
Patrick Hofman

176

ArgumentException그리고 그 파생물에 정말 유용합니다 .

public string DoSomething(string input) 
{
    if(input == null) 
    {
        throw new ArgumentNullException(nameof(input));
    }
    ...

이제 누군가 input매개 변수 이름을 리팩터링하면 예외도 최신 상태로 유지됩니다.

또한 속성이나 매개 변수의 이름을 얻기 위해 이전에 리플렉션을 사용해야했던 곳에서도 유용합니다.

귀하의 예 nameof(T)에서 type 매개 변수의 이름을 가져옵니다.이 또한 유용 할 수 있습니다.

throw new ArgumentException(nameof(T), $"Type {typeof(T)} does not support this method.");

또 다른 용도 nameof는 열거 형입니다-일반적으로 사용하는 열거 형의 문자열 이름을 원할 경우 .ToString():

enum MyEnum { ... FooBar = 7 ... }

Console.WriteLine(MyEnum.FooBar.ToString());

> "FooBar"

.Net이 열거 형 값 (예 :)을 보유하고 7런타임에 이름을 찾을 때 실제로는 상대적으로 느립니다 .

대신 다음을 사용하십시오 nameof.

Console.WriteLine(nameof(MyEnum.FooBar))

> "FooBar"

이제 .Net은 컴파일시 열거 이름을 문자열로 바꿉니다.


또 다른 용도는 INotifyPropertyChanged로깅 및 로깅에 사용됩니다. 두 경우 모두 호출하려는 멤버의 이름을 다른 메소드로 전달하려고합니다.

// Property with notify of change
public int Foo
{
    get { return this.foo; }
    set
    {
        this.foo = value;
        PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.Foo));
    }
}

또는...

// Write a log, audit or trace for the method called
void DoSomething(... params ...)
{
    Log(nameof(DoSomething), "Message....");
}

9
그리고 문자열 보간법에 또 다른 멋진 기능을 추가했습니다!
Patrick Hofman

1
@PatrickHofman과 typeof(T)비슷한 상황에서 유용한 컴파일 타임 설탕의 또 다른 조각입니다 :-)
Keith

1
내가 놓친 한 가지는 다음과 같습니다 nameofthismethod. 사용할 수는 Log.Error($"Error in {nameof(DoSomething)}...")있지만 다른 방법으로 복사하여 붙여 넣으면 여전히을 참조하고 있음을 알 수 없습니다 DoSomething. 따라서 로컬 변수 또는 매개 변수와 완벽하게 작동하는 동안 method-name은 문제입니다.
Tim Schmelter

3
존재 nameOf하는 경우 [DisplayName]속성 을 사용 할지 알고 있습니까? 예를 enum들어 [DisplayName]MVC 프로젝트에서 자주 사용 합니다
Luke T O'Brien

2
@AaronLS 예, 상당히 전문적이고 자주 사용하는 것이 아닙니다. 그러나 그것은 throw new완전히 다른 반 패턴 catch입니다. 주니어 개발자들에게는 문제를 해결하는 것처럼 느끼기 때문에 과도하게 사용 되는 것을 발견 했습니다 (대부분은 숨기고있을 때).
Keith

26

nameofC # 6.0의 기능이 유용한 또 다른 사용 사례 -DB 검색이 훨씬 쉬워지는 Dapper 와 같은 라이브러리를 고려하십시오 . 이 라이브러리는 훌륭한 라이브러리이지만 쿼리 내에 속성 / 필드 이름을 하드 코딩해야합니다. 이것이 의미하는 바는 속성 / 필드의 이름을 바꾸려는 경우 새 필드 이름을 사용하도록 쿼리를 업데이트하는 것을 잊을 가능성이 높다는 것입니다. 문자열 보간 및 nameof기능을 사용하면 코드를 유지 관리하고 입력하기가 훨씬 쉬워집니다.

링크에 주어진 예에서

이름없이

var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid });

이름으로

var dog = connection.Query<Dog>($"select {nameof(Dog.Age)} = @Age, {nameof(Dog.Id)} = @Id", new { Age = (int?)null, Id = guid });

3
나는 Dapper를 좋아하고 문자열 삽입을 정말 좋아하지만 IMO는 너무 못 생겼습니다. 열의 이름을 바꾸어 쿼리를 중단 할 위험은 그러한 못생긴 쿼리에 비해 너무 작습니다. 언뜻보기에 EF LINQ 쿼리를 작성하거나 [TableName]. [ColumnName]과 같은 규칙에 따라 필요할 때 쉽게 쿼리를 찾거나 바꿀 수 있습니다.
drizin

@drizin 나는 Dapper FluentMap 을 사용 하여 이러한 쿼리를 방지하고 우려를 분리합니다.
mamuesstack

21

귀하의 질문은 이미 목적을 나타냅니다. 이것이 예외를 기록하거나 던지는 데 유용 할 수 있음을 알아야합니다.

예를 들어.

public void DoStuff(object input)
{
    if (input == null)
    {
        throw new ArgumentNullException(nameof(input));
    }
}

이것은 좋은 내가 코드 대신 또는 잘못된 메시지와 함께 예외를 반환 중단 할 변수의 이름을 변경하는 경우 .


물론, 용도는이 간단한 상황에 한정되지 않습니다. nameof변수 또는 속성의 이름을 코딩하는 것이 유용 할 때마다 사용할 수 있습니다 .

다양한 바인딩 및 반사 상황을 고려할 때 용도가 다양합니다. 런타임 오류를 컴파일 타임에 가져 오는 훌륭한 방법입니다.


6
@atikot : 그러나 변수의 이름을 바꾸면 컴파일러는 문자열이 더 이상 일치하지 않음을 알 수 없습니다.
또는 매퍼

1
실제로 resharper를 사용하여 설명하지만 요점을 알 수 있습니다.
atikot

4
@atikot도 마찬가지이지만 Resharper는 컴파일러 오류가 아닌 경고 만 생성합니다. 확실성과 좋은 조언에는 차이가 있습니다.
Jodrell

1
@atikot, 그리고 Resharper는 로깅 메시지를 확인하지 않습니다
Jodrell

2
@ Jodrell : 그리고 코드 숨김, 사용자 정의 OnPropertyChanged메소드 ()가 아닌 속성 이름을 직접 수락 하는 WPF 바인딩 PropertyChangedEventArgs또는 특정 항목을 찾기 위해 리플렉션을 호출하여 WPF 바인딩을 사용하는 방법은 다양하지 않습니다. 회원 또는 유형?
또는 매퍼

13

내가 생각할 수있는 가장 일반적인 유스 케이스는 INotifyPropertyChanged인터페이스로 작업 할 때 입니다. (기본적으로 WPF 및 바인딩과 관련된 모든 것이이 인터페이스를 사용합니다)

이 예제를 살펴보십시오.

public class Model : INotifyPropertyChanged
{
    // From the INotifyPropertyChanged interface
    public event PropertyChangedEventHandler PropertyChanged;

    private string foo;
    public String Foo
    {
        get { return this.foo; }
        set
        {
            this.foo = value;
            // Old code:
            PropertyChanged(this, new PropertyChangedEventArgs("Foo"));

            // New Code:
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(Foo)));           
        }
    }
}

이전 방식에서 볼 수 있듯이 어떤 속성이 변경되었는지 나타내는 문자열을 전달해야합니다. 로 nameof우리가 직접 속성의 이름을 사용할 수 있습니다. 큰 문제는 아닌 것 같습니다. 그러나 누군가 속성 이름을 변경하면 어떤 일이 발생하는지 상상해보십시오 Foo. 문자열을 사용하면 바인딩이 작동하지 않지만 컴파일러는 경고하지 않습니다. nameof를 사용하면 name과 함께 속성 / 인수가 없다는 컴파일러 오류가 발생합니다 Foo.

일부 프레임 워크는 일부 리플렉션 매직을 사용하여 속성의 이름을 얻지 만 이제는 더 이상 필요하지 않은 이름이 있습니다.


5
이것이 유효한 접근 방법이지만보다 편리한 (그리고 DRY) 접근 방법은 [CallerMemberName]새로운 이벤트의 매개 변수에서이 이벤트를 발생 시키는 속성 을 사용하는 것입니다.
Drew Noakes

1
CallerMemberName도 훌륭하지만 별도의 유스 케이스입니다. (당신이 말했듯이) 메소드에서만 사용할 수 있기 때문입니다. DRY에 관해서 [CallerMemberName]string x = null는보다 나은지 확실하지 않습니다 nameof(Property). 속성 이름이 두 번 사용되었다고 말할 수는 있지만 기본적으로 인수가 함수에 전달되었습니다. 실제로 DRY의 의미는 아닙니다.
Roy T.

실제로 속성에서 사용할 수 있습니다. 그들도 회원입니다. 장점 nameof은 속성 설정자가 속성 이름을 전혀 지정할 필요가 없으므로 복사 / 붙여 넣기 버그가 발생하지 않는다는 것입니다.
Drew Noakes

4
그것은을위한 '더 나은 함께'상황의 INotifyPropertyChanged(가) 사용, [CallerMemberNameAttribute]그동안, 변경 알림이 깨끗하게 속성 setter에서 제기 할 수 있도록 nameof구문이 변경 알림을 깨끗하게 코드에서 다른 위치에서 제기 할 수 있습니다.
Andrew Hanlon

9

가장 일반적인 사용법은 다음과 같은 입력 검증에 있습니다.

//Currently
void Foo(string par) {
   if (par == null) throw new ArgumentNullException("par");
}

//C# 6 nameof
void Foo(string par) {
   if (par == null) throw new ArgumentNullException(nameof(par));
}

첫 번째 경우 par 매개 변수의 이름을 변경하는 메서드를 리팩터링 하면 ArgumentNullException 에서 변경하는 것을 잊어 버릴 수 있습니다 . 로 nameof 당신은 그것에 대해 걱정할 필요가 없습니다.

참조 : nameof (C # 및 Visual Basic 참조)


7

핵심은 ASP.NET MVC 프로젝트 사용 nameof에서 AccountController.csManageController.csRedirectToAction제어기에 동작을 참조하는 방법.

예:

return RedirectToAction(nameof(HomeController.Index), "Home");

이것은 다음과 같이 번역됩니다.

return RedirectToAction("Index", "Home");

'Home'컨트롤러에서 'Index'작업으로 이동합니다 /Home/Index.


왜 통째로 가서 사용하지 return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Substring(nameof(HomeController),0,nameof(HomeController).Length-"Controller".Length));않습니까?
Suncat2000

@ Suncat2000 그 중 하나는 컴파일에서 수행되고 다른 하나는 그렇지 않기 때문에? :)
Dinerdo

6

다른 사람들이 이미 지적했듯이 nameof연산자는 요소가 소스 코드에 주어진 이름을 삽입합니다.

이 문자열 리팩토링이 안전하기 때문에 리팩토링 측면에서 이것이 정말 좋은 아이디어라고 덧붙이고 싶습니다. 이전에는 동일한 목적으로 리플렉션을 사용하는 정적 메서드를 사용했지만 런타임 성능에 영향을 미쳤습니다. nameof연산자에는 런타임 성능에 영향을주지 않는다; 컴파일 타임에 작동합니다. MSIL코드를 보면 문자열이 포함되어 있습니다. 다음 방법과 분해 된 코드를 참조하십시오.

static void Main(string[] args)
{
    Console.WriteLine(nameof(args));
    Console.WriteLine("regular text");
}

// striped nops from the listing
IL_0001 ldstr args
IL_0006 call System.Void System.Console::WriteLine(System.String)
IL_000C ldstr regular text
IL_0011 call System.Void System.Console::WriteLine(System.String)
IL_0017 ret

그러나 소프트웨어를 난독 화하려는 경우 단점이 될 수 있습니다. 난독 처리 후 포함 된 문자열이 더 이상 요소 이름과 일치하지 않을 수 있습니다. 이 본문에 의존하는 메커니즘은 깨질 것입니다. 이에 대한 예는 다음과 같습니다. Reflection, NotifyPropertyChanged ...

런타임 동안 이름을 결정하면 성능이 약간 떨어지지 만 난독 화에 안전합니다. 난독 처리가 필요하거나 계획되지 않은 경우 nameof운영자를 사용하는 것이 좋습니다 .


5

코드에서 변수를 사용하고 변수의 이름을 가져 와서 인쇄해야한다고 생각해야합니다.

int myVar = 10;
print("myVar" + " value is " + myVar.toString());

그리고 누군가가 코드를 리팩토링하고 "myVar"라는 다른 이름을 사용하면 코드에서 문자열 값을 감시하고 그에 따라 코드를 작성해야합니다.

대신에

print(nameof(myVar) + " value is " + myVar.toString());

자동 리팩토링하는 데 도움이됩니다!


소스 코드 표현 Type, 및 값을 포함하는 각 매개 변수마다 하나씩 튜플 배열을 전달하는 특수 변수 매개 변수 구문이 있었으면 좋겠습니다 . 따라서 로깅 메소드를 호출하는 코드에서 많은 중복성을 제거 할 수 있습니다.
supercat

5

MSDN 기사 에는 MVC 라우팅 (실제로 나를 위해 개념을 클릭 한 예)이 나와 있습니다. (형식화 된) 설명 단락은 다음과 같습니다.

  • 코드에서 오류를보고 할 때
  • MVC (Model-View-Controller) 링크 연결
  • 발사 속성 변경 이벤트 등

종종 메소드의 문자열 이름캡처 하려고합니다 . nameof를 사용하면 정의 이름을 바꿀 때 코드를 유효하게 유지할 수 있습니다.

문자열 리터럴 을 사용하여 정의를 참조 하기 전에 코드 요소의 이름을 바꿀 때 취하기 가 쉽지 않습니다. 도구는 이러한 문자열 리터럴을 확인할 수 없기 때문입니다.

인정 / 최고 등급의 답변은 이미 몇 가지 훌륭한 구체적인 예를 제공합니다.


3

nameof운영자 의 목적은 아티팩트의 소스 이름을 제공하는 것입니다.

일반적으로 소스 이름은 메타 데이터 이름과 동일한 이름입니다.

public void M(string p)
{
    if (p == null)
    {
        throw new ArgumentNullException(nameof(p));
    }
    ...
}

public int P
{
    get
    {
        return p;
    }
    set
    {
        p = value;
        NotifyPropertyChanged(nameof(P));
    }
}

그러나 항상 그런 것은 아닙니다.

using i = System.Int32;
...
Console.WriteLine(nameof(i)); // prints "i"

또는:

public static string Extension<T>(this T t)
{
    return nameof(T); returns "T"
}

내가 줄 한 가지 용도는 리소스의 이름을 지정하는 것입니다.

[Display(
    ResourceType = typeof(Resources),
    Name = nameof(Resources.Title_Name),
    ShortName = nameof(Resources.Title_ShortName),
    Description = nameof(Resources.Title_Description),
    Prompt = nameof(Resources.Title_Prompt))]

사실이 경우 리소스에 액세스하기 위해 생성 된 속성이 필요하지 않았지만 이제는 리소스가 존재하는지 컴파일 시간을 확인합니다.


0

nameof키워드 사용법 중 하나는 프로그래밍 방식Binding 으로 wpf에서 설정하는 것 입니다 .

세트 Binding는 세트에있는 Path문자열과 함께 nameof키워드는 리팩터링 옵션을 사용할 수 있습니다.

예를 들어에 IsEnable종속성 속성이 있고 의 일부 에 UserControl바인딩하려는 경우 다음 두 코드를 사용할 수 있습니다.IsEnableCheckBoxUserControl

CheckBox chk = new CheckBox();
Binding bnd = new Binding ("IsEnable") { Source = this };
chk.SetBinding(IsEnabledProperty, bnd);

CheckBox chk = new CheckBox();
Binding bnd = new Binding (nameof (IsEnable)) { Source = this };
chk.SetBinding(IsEnabledProperty, bnd);

첫 번째 코드는 리팩터링 할 수 없지만 secend 코드는 ...


0

이전에는 다음과 같은 것을 사용했습니다.

// Some form.
SetFieldReadOnly( () => Entity.UserName );
...
// Base form.
private void SetFieldReadOnly(Expression<Func<object>> property)
{
    var propName = GetPropNameFromExpr(property);
    SetFieldsReadOnly(propName);
}

private void SetFieldReadOnly(string propertyName)
{
    ...
}

이유-컴파일 시간 안전. 아무도 자동으로 속성 이름을 바꾸고 코드 논리를 중단 할 수 없습니다. 이제 nameof ()를 사용할 수 있습니다.


0

ASP.Net MVC를 사용할 때 이점이 있습니다. HTML 도우미를 사용하여 일부 컨트롤을 볼 때 HTML 입력의 이름 속성에 속성 이름이 사용됩니다.

@Html.TextBoxFor(m => m.CanBeRenamed)

그것은 다음과 같은 것을 만듭니다 :

<input type="text" name="CanBeRenamed" />

이제 Validate 메소드에서 속성을 확인해야하는 경우 다음을 수행 할 수 있습니다.

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
  if (IsNotValid(CanBeRenamed)) {
    yield return new ValidationResult(
      $"Property {nameof(CanBeRenamed)} is not valid",
      new [] { $"{nameof(CanBeRenamed)}" })
  }
}

리팩토링 도구를 사용하여 속성 이름을 바꾸는 경우 유효성 검사가 중단되지 않습니다.


0

또 다른 사용 사례는 nameof색인을 확인하는 대신 탭 페이지를 확인하는 것입니다 Name. 다음과 같이 탭 페이지의 속성을 확인할 수 있습니다 .

if(tabControl.SelectedTab.Name == nameof(tabSettings))
{
    // Do something
}

덜 지저분한 :)


0

nameof내 응용 프로그램에서 매우 길고 복잡한 SQL 문의 가독성 을 높입니다. 변수가 문자열에서 돋보이게하고 SQL 문에서 변수가 사용되는 위치를 파악하지 않아도됩니다.

public bool IsFooAFoo(string foo, string bar)
{
    var aVeryLongAndComplexQuery = $@"SELECT yada, yada
    -- long query in here
    WHERE fooColumn = @{nameof(foo)}
    AND barColumn = @{nameof(bar)}
    -- long query here";


    SqlParameter[] parameters = {
        new SqlParameter(nameof(foo), SqlDBType.VarChar, 10){ Value = foo },
        new SqlParameter(nameof(bar), SqlDBType.VarChar, 10){ Value = bar },
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.