ご存知のようint
に、ベースタイプのメソッドToString()
をオーバーライドするメソッドがあります。ToString()
Object
この次のコードでは、
int x = 100;
object y = (object)x;
Console.Write(y.ToString());
(1)誰ToString()
が呼ばれるのですか?intまたはobject?なぜ?
(2)どうすれば真実を確認/表示できますか?デバッグ/ツールによって?
Int32.ToString()
と呼ばれるのは、ToString()
事実上と呼ばれるからです。
Int32.ToString()
をオーバーライドObject.ToString()
するため、実行時に。を効果的に置き換えObject.ToString()
ます。
そして実行時にy
、ボックス化されていますint
。
値がボックス化されているため、コンパイラーが認識しているのはですobject
。これは、への通常の仮想呼び出しであり、構造体object.ToString()
のオーバーライドToString()
を取得します。つまり、呼び出されるobject.ToString()
のは、実行されるオーバーライドです。Int32.ToString()
private static void Main()
{
int x = 100;
object y = (object)x;
Console.Write(y.ToString());
}
になる(私のコメント):
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] int32 x,
[1] object y)
// int x = 100;
L_0000: ldc.i4.s 100
L_0002: stloc.0
// object y = (object)x;
L_0003: ldloc.0
L_0004: box int32
L_0009: stloc.1
// Console.Write(y.ToString());
L_000a: ldloc.1
L_000b: callvirt instance string [mscorlib]System.Object::ToString()
L_0010: call void [mscorlib]System.Console::Write(string)
L_0015: ret
}
重要な行はL_000b
;にあります。への通常の仮想呼び出しobject.ToString()
。
さらに興味深いのは、ボックス化されていない値型です。値型がを持っていることがわかっToString()
ている場合、静的呼び出しを発行できます。
private static void Main()
{
int x = 100;
Console.Write(x.ToString());
}
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] int32 x)
L_0000: ldc.i4.s 100
L_0002: stloc.0
L_0003: ldloca.s x
L_0005: call instance string [mscorlib]System.Int32::ToString()
L_000a: call void [mscorlib]System.Console::Write(string)
L_000f: ret
}
のstatic-call(call
)を参照してくださいL_0005
。ただし、ほとんどの値型の場合は、制約付き呼び出しを使用します。これは、オーバーライドされた場合は静的呼び出しとして、そうでない場合は仮想呼び出しとしてJITによって解釈されます。
private static void Main()
{
var x = new KeyValuePair<int, string>(123, "abc");
Console.Write(x.ToString());
}
になります:
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 3
.locals init (
[0] valuetype [mscorlib]System.Collections.Generic.KeyValuePair`2<int32, string> x)
L_0000: ldloca.s x
L_0002: ldc.i4.s 0x7b
L_0004: ldstr "abc"
L_0009: call instance void [mscorlib]System.Collections.Generic.KeyValuePair`2<int32, string>::.ctor(!0, !1)
L_000e: ldloca.s x
L_0010: constrained [mscorlib]System.Collections.Generic.KeyValuePair`2<int32, string>
L_0016: callvirt instance string [mscorlib]System.Object::ToString()
L_001b: call void [mscorlib]System.Console::Write(string)
L_0020: ret
}
「constrained」/「callvirt」のペアがL_0010
一緒L_0016
になってこの構成を作成するため、実際には仮想呼び出しではない可能性があります。JIT /ランタイムは、他のブードゥーを実行できます。これについては、こちらで詳しく説明します。
基本タイプの実装への静的呼び出しであるシナリオを除いて、通常class
は常にこれに仮想呼び出しを使用することに注意してください。return base.ToString();
Int32、それは同じコントラクトを満たす下の実際のオブジェクトだからです。出力を見て確認できます。そうでない場合は、代わりに「System.Object」が返されます。
それはJavaだと思った。ごめん!:)intはプリミティブ型です。すべてのプリミティブタイプは、OOの魔法が止まる唯一の例外です。それらはオブジェクトを継承せず、オブジェクトではありません!(toStringメソッドなし、一般的なメソッドなし)
整数、toStringメソッドがあります。例がintではなくIntegerを使用していて、オブジェクトがクラスにキャストされた場合(例で行ったように)、そのクラスがtoStringをオーバーライドする場合(たとえば、カスタムクラス)、またはすでにtoStringメソッドを持っている場合(Integerなど) class)、そのクラスtoStringメソッドを呼び出す必要があります。
それ以外の場合は、継承元のクラスのtoStringメソッドを呼び出す必要があります。nobodyから継承されなかった場合(つまり、Objectクラスから継承された場合)、オブジェクトクラスtoStringを呼び出す必要があります。
いくつかのプリントを使用して簡単なプログラムを作成すると、次のことが役立ちます。
Integer x = new Integer(3);
印刷:
x.toString()
((Object) x).toString();
そして、新しい匿名クラス、Integerと同じですが、toString Overriden
Integer y = new Integer(3){
@Override
String toString(){
return "WHATEVER!!";
}
};
印刷:
y.toString();