私が間違っているなら私を訂正しますが、次のようなことをします
var typeOfName = typeof(Foo).Name;
そして
var nameOfName = nameof(Foo);
まったく同じ出力が得られるはずです。このソースによると理解できる理由の1つ: https://msdn.Microsoft.com/en-us/library/dn986596.aspx は
"nameofを使用すると、定義の名前を変更するときにコードを有効に保つことができます"
クラスインスタンスを文字列として取得したい場合、できないそのようなことをすることは可能です:
var fooInstance = new Foo();
var nameOfName = nameof(fooInstance);
ただし、次のようなことができます。
static string GetName<T>(T item) where T : class
{
return typeof(T).GetProperties()[0].Name;
}
var typeOfName2 = GetName(new { fooInstance });
どちらの場合でも(typeof
とnameof
)リファクタリングが可能であるため、nameof
などのより高いレベルのキーワードを再発明して、もう存在している。それらの間に私がはっきりと見ていない違いはありますか?
最後に、誰かがnameof
の実装を確認するために参照ソースを私に指摘してもらえれば幸いです。リフレクションを使用していますか?
更新1:here から取得
nameof
は、文字列変数を宣言するのと同じくらい効率的です。何の反射もない!
var firstname = "Gigi";
var varname = nameof(firstname);
Console.WriteLine(varname); // Prints "firstname" to the console
生成されたMSILをチェックアウトすると、文字列へのオブジェクト参照がldstr演算子を使用してスタックにプッシュされるため、それが文字列宣言と同等であることがわかります。
IL_0001: ldstr "Gigi"
IL_0006: stloc.0
IL_0007: ldstr "firstname"
IL_000c: stloc.1
IL_000d: ldloc.1
IL_000e: call void [mscorlib]System.Console::WriteLine(string)
2つの理由:
nameof
はコンパイル時の定数に変わります。 typeof(...).Name
には、少しリフレクションが必要です。 過度に高くはありませんが、場合によっては害を及ぼす可能性があります。
次に、型名以外にも使用されます。たとえば、引数:
void SomeMethod(int myArgument)
{
Debug.WriteLine(nameof(myArgument));
}
クラスのメンバーや地元の人の名前も取得できます。言うまでもなく、これは情報のデバッグに非常に役立ちます。これは、たとえば、以下の場合に脆弱性の少ない反射を実装する方法の1つでもあります。式ツリーの解析(残念なことに、これを使用するプロジェクトでは、C#5を使用した.NET 4.0のままです-あちこちにいくつかのハックを保存することができます)。
混乱を解消するために、nameof
はnot関数であり、どちらもtypeof
ではありません。これはコンパイル時の演算子であり、常にコンパイル時に評価されます(ただし、ジェネリックスは「コンパイル時」を少し先に進めます)。
BenchmarkDotNetを使用したベンチマークテストは次のとおりです
// * Summary *
Host Process Environment Information:
BenchmarkDotNet.Core=v0.9.9.0
OS=Windows
Processor=?, ProcessorCount=8
Frequency=2740584 ticks, Resolution=364.8857 ns, Timer=TSC
CLR=CORE, Arch=64-bit ? [RyuJIT]
GC=Concurrent Workstation
dotnet cli version: 1.0.0-preview2-003133
Type=GetNameBenchmark Mode=Throughput LaunchCount=2
WarmupCount=10 TargetCount=200
Method | Median | StdDev |
----------- |----------- |---------- |
TypeOf | 16.0348 ns | 0.7896 ns |
NameOf | 0.0005 ns | 0.0147 ns |
リフレクションを使用して文字列を生成することは可能ですが、あまりエレガントではなく、常に可能であるとは限りません。たとえば、サンドボックス化されたコードでReflectionを使用することはできません。また、ローカル変数では使用できません。そしてそれは高価です。
nameof
演算子はコンパイル時に機能します。コンパイラは、コードを解析するときにすでに名前を知っています。したがって、文字列リテラルを簡単に生成できます。非常に高速で、高速になることはありません。実行時の制限はありません。
それらの間にはいくつかの違いがありますが、それらは主に背後にある実際的な理由です。 例1:
次のようなものを書く方がエレガントです
switch (e.ArgumentPropertyName)
{
case nameof(aProperty):
break;
case "anotherProperty":
break;
}
anotherProperty
タイプをリファクタリングして強打してください! nameof
は変更を反映し、"anotherProperty"
はサイレントに渡され、コードはそのcaseステートメント内で実行されることはありません。
例2:
enum MetalEnum { Gold = 1, Silver = 2, ... }
どちらがいいですか?
Console.WriteLine(MetalEnum.Gold.ToString()); // finds the name at runtime (slower)
または
Console.WriteLine(nameof(MetalEnum.Gold)); // compile time (faster)
例3:
最後に、次のようなものを書くのがどれほど醜いか覚えていますか
PropertyChanged(this, new PropertyChangedEventArgs("Foo"));
これで、次のように美しく書くことができます。
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Foo)));
ドキュメント によると:
変数、型、またはメンバーの単純な(非修飾)文字列名を取得するために使用されます。
...
nameofの引数は、単純名、修飾名、メンバーアクセス、指定されたメンバーのベースアクセス、または指定されたメンバーのこのアクセスでなければなりません。引数式はコード定義を識別しますが、評価されることはありません。
引数は構文的に式である必要があるため、リストするのに役立たない多くの許可されていないものがあります。エラーを生成することについて言及する価値があります:事前定義された型(たとえば、intまたはvoid)、null許容型(
Point?
)、配列タイプ(Customer[,]
)、ポインタ型(Buffer*
)、修飾エイリアス(A::B
)、およびバインドされていないジェネリック型(Dictionary<,>
)、前処理記号(DEBUG
)、およびラベル(loop:
)。
nameofが取得する単純な名前は、メタデータ名ではなくソース名です。
したがって、このコード:
using Integer = System.Int32;
var s = "a string";
Console.WriteLine(nameof(s));
Console.WriteLine(nameof(Integer));
Console.WriteLine(nameof(System.Int32));
void M<T>() { Console.WriteLine(nameof(T)); }
M<int>();
M<string>();
印刷されます:
s
Integer
Int32
T
T
TypeofはTypeオブジェクトを返します。多くの場合、パラメーターとして、または変数やフィールドとして使用されます。 typeof演算子は、Typeポインターを取得する式の一部です。
class Program
{
static Type _type = typeof(char); // Store Type as field.
static void Main()
{
Console.WriteLine(_type); // Value type pointer
Console.WriteLine(typeof(int)); // Value type
Console.WriteLine(typeof(byte)); // Value type
Console.WriteLine(typeof(Stream)); // Class type
Console.WriteLine(typeof(TextWriter)); // Class type
Console.WriteLine(typeof(Array)); // Class type
Console.WriteLine(typeof(int[])); // Array reference type
}
}
出力
System.Char
System.Int32
System.Byte
System.IO.Stream
System.IO.TextWriter
System.Array
System.Int32[]
Nameofは、変数名を含む文字列を返します。コンパイル時に機能します。これは、一部のプログラムを簡略化する特別なコンパイラー機能です。
int size=100;
Console.WriteLine(nameof(size));
出力:サイズ
TypeofはTypeオブジェクトを返します。多くの場合、パラメーターとして、または変数やフィールドとして使用されます。 typeof演算子は、Typeポインターを取得する式の一部です。
一方、Nameofは、変数の名前を含む文字列を返します。コンパイル時に機能します。これは、一部のプログラムを簡略化する特別なコンパイラー機能です。