By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
459,296 Members | 1,476 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 459,296 IT Pros & Developers. It's quick & easy.

shouldn't if (this!=null) generate a warning?

P: n/a
Shouldn't if (this!=null) generate a compiler warning?

in fact, it does not.
May 26 '06 #1
Share this Question
Share on Google+
16 Replies


P: n/a

cody wrote:
Shouldn't if (this!=null) generate a compiler warning?


What warning would you like to see?

--
Larry Lard
Replies to group please

May 26 '06 #2

P: n/a
"Larry Lard" <la*******@hotmail.com> wrote in message
news:11*********************@i39g2000cwa.googlegro ups.com...
cody wrote:
Shouldn't if (this!=null) generate a compiler warning?


What warning would you like to see?


I see what the OP is driving at - sort of like asking Descartes if it's
possible to think you don't exist... :-)
May 26 '06 #3

P: n/a

Mark Rae wrote:
"Larry Lard" <la*******@hotmail.com> wrote in message
news:11*********************@i39g2000cwa.googlegro ups.com...
cody wrote:
Shouldn't if (this!=null) generate a compiler warning?


What warning would you like to see?


I see what the OP is driving at - sort of like asking Descartes if it's
possible to think you don't exist... :-)


I see what they're driving at too, and I'm trying to be Socratic :)

--
Larry Lard
Replies to group please

May 26 '06 #4

P: n/a

"cody" <de********@gmx.de> wrote in message
news:uS**************@TK2MSFTNGP05.phx.gbl...
Shouldn't if (this!=null) generate a compiler warning?

in fact, it does not.

Maybe we need a "something" to go along with "null", then we could just do
if (this == something)

:-)
May 26 '06 #5

P: n/a
the same warning that is generated for other expressions which the compiler
determines to always be true or to alway be false.
for example an expression like "if (this is string)" will generate a warning
(if the class is not system.string or course).

--

"Larry Lard" <la*******@hotmail.com> schrieb im Newsbeitrag
news:11*********************@i39g2000cwa.googlegro ups.com...

cody wrote:
Shouldn't if (this!=null) generate a compiler warning?


What warning would you like to see?

--
Larry Lard
Replies to group please

May 26 '06 #6

P: n/a
"james" <ja***@com.com> wrote in message
news:44**********************@news.zen.co.uk...
Maybe we need a "something" to go along with "null", then we could just do
if (this == something)


Ah yes, but just because something isn't nothing doesn't imply that it is
something...

You know what they say: "you can take a horse to water but a pencil must be
lead"

And, for the extremely politically incorrect among us, "one in Kate Bush is
worth ten in the hand..."
May 26 '06 #7

P: n/a
>the same warning that is generated for other expressions which the compiler
determines to always be true or to alway be false.


But in this case the condition can evaluate to false. While you're not
allowed to do it in C#, the CLR and other languages let you call
instance methods on a null reference.
Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.
May 26 '06 #8

P: n/a
Great. So we would always have to put if (this!=null) throw new
ArgumentNullException(); in every method just to make sure :-)

"Mattias Sjögren" <ma********************@mvps.org> schrieb im Newsbeitrag
news:uM**************@TK2MSFTNGP05.phx.gbl...
the same warning that is generated for other expressions which the
compiler
determines to always be true or to alway be false.


But in this case the condition can evaluate to false. While you're not
allowed to do it in C#, the CLR and other languages let you call
instance methods on a null reference.
Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.

May 27 '06 #9

P: n/a
Mattias Sjögren <ma********************@mvps.org> wrote in
news:uM**************@TK2MSFTNGP05.phx.gbl:
the same warning that is generated for other expressions which the
compiler determines to always be true or to alway be false.


But in this case the condition can evaluate to false. While you're not
allowed to do it in C#, the CLR and other languages let you call
instance methods on a null reference.
Mattias


But we're discussing the CSharp compiler...
May 27 '06 #10

P: n/a
Martin Milan <I8**@m.i.do> wrote:
But we're discussing the CSharp compiler...


A class written with C#, and compiled with the C# compiler, may be
instantiated and invoked from another language targeting the CLR. That
language may invoke a method on a null instance.

Basically, the C# compiler always uses the CIL instruction 'callvirt'
for calling instance methods, even on non-virtual methods. Other
compilers may use 'call' rather than 'callvirt'.

There is a difference between using 'call' and 'callvirt': 'callvirt'
throws an exception if the instance is null, while 'call' doesn't.

---8<---
..assembly extern mscorlib {}
..assembly Test {}
..subsystem 0x0003

..class App extends [mscorlib]System.Object
{
.method public instance void Test()
{
ldstr "This is null\? {0}"

ldarg.0
ldnull
ceq
box [mscorlib]System.Boolean
call void [mscorlib]System.Console::WriteLine(string,object)
ret
}

.method public instance void .ctor()
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}

.method public static void Main()
{
.entrypoint

ldstr "First:"
call void [mscorlib]System.Console::WriteLine(string)
ldnull
call instance void App::Test()

ldstr "Second:"
call void [mscorlib]System.Console::WriteLine(string)
ldnull
callvirt instance void App::Test()
ret
}
}
--->8---

Compile with ilasm, and execute:

---8<---
First:
This is null? True
Second:

Unhandled Exception: System.NullReferenceException: Object reference not
set to an instance of an object.
at App.Main()
--->8---

-- Barry

--
http://barrkel.blogspot.com/
May 27 '06 #11

P: n/a
>Great. So we would always have to put if (this!=null) throw new
ArgumentNullException(); in every method just to make sure :-)


Personally I'm happy with the NullReferenceException that will be
thrown the first time I try to access a field.
Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.
May 27 '06 #12

P: n/a
> Basically, the C# compiler always uses the CIL instruction 'callvirt'
for calling instance methods, even on non-virtual methods. Other
compilers may use 'call' rather than 'callvirt'.

There is a difference between using 'call' and 'callvirt': 'callvirt'
throws an exception if the instance is null, while 'call' doesn't.


wouldn't that mean that a method call in c# is slower than that one in other
languages due to the extra vtable lookup?
May 28 '06 #13

P: n/a
"cody" <de********@gmx.de> wrote:
Basically, the C# compiler always uses the CIL instruction 'callvirt'
for calling instance methods, even on non-virtual methods. Other
compilers may use 'call' rather than 'callvirt'.

There is a difference between using 'call' and 'callvirt': 'callvirt'
throws an exception if the instance is null, while 'call' doesn't.


wouldn't that mean that a method call in c# is slower than that one in other
languages due to the extra vtable lookup?


It's not looking up the vtable (non-virtual methods aren't in the
vtable), but it would be ever so slightly slower, yes.

-- Barry

--
http://barrkel.blogspot.com/
May 29 '06 #14

P: n/a
>> > Basically, the C# compiler always uses the CIL instruction 'callvirt'
> for calling instance methods, even on non-virtual methods. Other
> compilers may use 'call' rather than 'callvirt'.
>
> There is a difference between using 'call' and 'callvirt': 'callvirt'
> throws an exception if the instance is null, while 'call' doesn't.


wouldn't that mean that a method call in c# is slower than that one in
other
languages due to the extra vtable lookup?


It's not looking up the vtable (non-virtual methods aren't in the
vtable), but it would be ever so slightly slower, yes.


yes they aren't, but how can the instruction know wheather the method is a
virtual one or not without looking into the vtable or is this resolved at
jit time?
May 29 '06 #15

P: n/a
"cody" <de********@gmx.de> wrote:
It's not looking up the vtable (non-virtual methods aren't in the
vtable), but it would be ever so slightly slower, yes.
yes they aren't, but how can the instruction know wheather the method is a
virtual one or not without looking into the vtable or is this resolved at
jit time?


It is resolved at JIT time, by looking up the method information in
metadata. My investigation follows:

What's the difference in JIT-compiled code between 'call' and 'callvirt'
on CLR 2.0.50727?

I started with this CIL:

---8<---
..assembly extern mscorlib {}
..assembly Test {}
..subsystem 0x0003

..class App extends [mscorlib]System.Object
{
.method public instance void Test()
{
ldstr "This is null\? {0}"

ldarg.0
ldnull
ceq
box [mscorlib]System.Boolean
call void [mscorlib]System.Console::WriteLine(string,object)
ret
}

.method public static void Main()
{
.entrypoint

ldstr "First:"
call void [mscorlib]System.Console::WriteLine(string)
ldnull
call instance void App::Test()

ldstr "Second:"
call void [mscorlib]System.Console::WriteLine(string)
ldnull
callvirt instance void App::Test()
ret
}
}
--->8---

Roughly transliterated into C# code, it looks like this:

---8<---
using System;

class App
{
public void Test()
{
Console.WriteLine("This is null? {0}", this == null);
}

public static void Main()
{
Console.WriteLine("First:");
((App) null).Test(); // with 'call': not possible in MS C# 2.0
Console.WriteLine("Second:");
((App) null).Test(); // with 'callvirt': default for C# compiler
}
}
--->8---

This assembly's name is Test, but I compiled it to an executable called
CallVirt.exe, with ilasm, and started the VS 2005 debugger:

---8<---
ilasm -debug=opt CallVirt.il
devenv -debugexe CallVirt.exe
--->8---

I changed the project's debugger settings from Auto to Mixed, and
stepped into the code. When disassembled with SOS, the code for the
App.Main method looks like this:

---8<---
..load sos
extension C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos. dll loaded

!name2ee CallVirt.exe App.Main
PDB symbol for mscorwks.dll not loaded
Module: 00912c14 (CallVirt.exe)
Token: 0x06000002
MethodDesc: 00912fe0
Name: App.Main()
JITTED Code Address: 00de0070

!u 00de0070
Normal JIT generated code
App.Main()
Begin 00de0070, size 61
00DE0070 833D84102B0200 cmp dword ptr ds:[022B1084h],0 00DE0077 750A jne 00DE0083
00DE0079 B901000000 mov ecx,1
00DE007E E889D75678 call 7934D80C
(System.Console.InitializeStdOutError(Boolean), mdToken: 0600070f)
00DE0083 8B0D84102B02 mov ecx,dword ptr ds:[022B1084h]
00DE0089 8B153C302B02 mov edx,dword ptr ds:[022B303Ch]
00DE008F 8B01 mov eax,dword ptr [ecx]
00DE0091 FF90D8000000 call dword ptr [eax+000000D8h]
00DE0097 33C9 xor ecx,ecx
00DE0099 FF1520309100 call dword ptr ds:[00913020h]
00DE009F 833D84102B0200 cmp dword ptr ds:[022B1084h],0
00DE00A6 750A jne 00DE00B2
00DE00A8 B901000000 mov ecx,1
00DE00AD E85AD75678 call 7934D80C
(System.Console.InitializeStdOutError(Boolean), mdToken: 0600070f)
00DE00B2 8B0D84102B02 mov ecx,dword ptr ds:[022B1084h]
00DE00B8 8B1540302B02 mov edx,dword ptr ds:[022B3040h]
00DE00BE 8B01 mov eax,dword ptr [ecx]
00DE00C0 FF90D8000000 call dword ptr [eax+000000D8h]
00DE00C6 33C9 xor ecx,ecx
00DE00C8 3909 cmp dword ptr [ecx],ecx
00DE00CA FF1520309100 call dword ptr ds:[00913020h]
00DE00D0 C3 ret
--->8---

This code is longer that strictly "necessary" because the
Console::WriteLine(string) method has been inlined. The two relevant
snippets of code for 'call' and 'callvirt', including the setting of the
'this' argument to null, are as follows:

---8<---
// CALL
00DE0097 33C9 xor ecx,ecx
00DE0099 FF1520309100 call dword ptr ds:[00913020h]

// CALLVIRT
00DE00C6 33C9 xor ecx,ecx
00DE00C8 3909 cmp dword ptr [ecx],ecx
00DE00CA FF1520309100 call dword ptr ds:[00913020h]
--->8---

The difference with CALLVIRT is that it tests the pointer by
dereferencing it. That causes a hardware exception when the pointer is
null, and that hardware exception gets propagated to the CLR via Windows
SEH.

Something interesting that can be observed from this: the calls to the
App.Test() method are through an indirection. A peek in the address
shows the data:

---8<---d -format:fourbytes 0x00913020

0x00913020 00de00e8 00de0070 00000080 022b1ec4
0x00913030 912fd8b8 e9ed8900 ffa2eed0 912fe0b8
0x00913040 e9ed8900 ffa2eec4 576f62e8 cccc5e79
0x00913050 00912fe0 00000000 00000000 00000000
--->8---

One can then disassemble the code at the indirect location:

---8<---
!u 0x00de00e8

Normal JIT generated code
App.Test()
Begin 00de00e8, size 44 00DE00E8 57 push edi


.... etc.
--->8---

So, calls to non-virtual instance methods compiled with the current C#
compiler get turned into CIL 'callvirt' instructions, which, with the
current JIT compiler, test the 'this' argument with a CMP instruction.

-- Barry

--
http://barrkel.blogspot.com/
May 29 '06 #16

P: n/a
very interesting! thank you for this great info!

--
"Barry Kelly" <ba***********@gmail.com> schrieb im Newsbeitrag
news:ms********************************@4ax.com...
"cody" <de********@gmx.de> wrote:
> It's not looking up the vtable (non-virtual methods aren't in the
> vtable), but it would be ever so slightly slower, yes.


yes they aren't, but how can the instruction know wheather the method is
a
virtual one or not without looking into the vtable or is this resolved at
jit time?


It is resolved at JIT time, by looking up the method information in
metadata. My investigation follows:

What's the difference in JIT-compiled code between 'call' and 'callvirt'
on CLR 2.0.50727?

I started with this CIL:

---8<---
.assembly extern mscorlib {}
.assembly Test {}
.subsystem 0x0003

.class App extends [mscorlib]System.Object
{
.method public instance void Test()
{
ldstr "This is null\? {0}"

ldarg.0
ldnull
ceq
box [mscorlib]System.Boolean
call void [mscorlib]System.Console::WriteLine(string,object)
ret
}

.method public static void Main()
{
.entrypoint

ldstr "First:"
call void [mscorlib]System.Console::WriteLine(string)
ldnull
call instance void App::Test()

ldstr "Second:"
call void [mscorlib]System.Console::WriteLine(string)
ldnull
callvirt instance void App::Test()
ret
}
}
--->8---

Roughly transliterated into C# code, it looks like this:

---8<---
using System;

class App
{
public void Test()
{
Console.WriteLine("This is null? {0}", this == null);
}

public static void Main()
{
Console.WriteLine("First:");
((App) null).Test(); // with 'call': not possible in MS C# 2.0
Console.WriteLine("Second:");
((App) null).Test(); // with 'callvirt': default for C# compiler
}
}
--->8---

This assembly's name is Test, but I compiled it to an executable called
CallVirt.exe, with ilasm, and started the VS 2005 debugger:

---8<---
ilasm -debug=opt CallVirt.il
devenv -debugexe CallVirt.exe
--->8---

I changed the project's debugger settings from Auto to Mixed, and
stepped into the code. When disassembled with SOS, the code for the
App.Main method looks like this:

---8<---
.load sos
extension C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos. dll loaded

!name2ee CallVirt.exe App.Main
PDB symbol for mscorwks.dll not loaded
Module: 00912c14 (CallVirt.exe)
Token: 0x06000002
MethodDesc: 00912fe0
Name: App.Main()
JITTED Code Address: 00de0070

!u 00de0070
Normal JIT generated code
App.Main()
Begin 00de0070, size 61
00DE0070 833D84102B0200 cmp dword ptr ds:[022B1084h],0 00DE0077 750A jne 00DE0083
00DE0079 B901000000 mov ecx,1
00DE007E E889D75678 call 7934D80C
(System.Console.InitializeStdOutError(Boolean), mdToken: 0600070f)
00DE0083 8B0D84102B02 mov ecx,dword ptr ds:[022B1084h]
00DE0089 8B153C302B02 mov edx,dword ptr ds:[022B303Ch]
00DE008F 8B01 mov eax,dword ptr [ecx]
00DE0091 FF90D8000000 call dword ptr [eax+000000D8h]
00DE0097 33C9 xor ecx,ecx
00DE0099 FF1520309100 call dword ptr ds:[00913020h]
00DE009F 833D84102B0200 cmp dword ptr ds:[022B1084h],0
00DE00A6 750A jne 00DE00B2
00DE00A8 B901000000 mov ecx,1
00DE00AD E85AD75678 call 7934D80C
(System.Console.InitializeStdOutError(Boolean), mdToken: 0600070f)
00DE00B2 8B0D84102B02 mov ecx,dword ptr ds:[022B1084h]
00DE00B8 8B1540302B02 mov edx,dword ptr ds:[022B3040h]
00DE00BE 8B01 mov eax,dword ptr [ecx]
00DE00C0 FF90D8000000 call dword ptr [eax+000000D8h]
00DE00C6 33C9 xor ecx,ecx
00DE00C8 3909 cmp dword ptr [ecx],ecx
00DE00CA FF1520309100 call dword ptr ds:[00913020h]
00DE00D0 C3 ret
--->8---

This code is longer that strictly "necessary" because the
Console::WriteLine(string) method has been inlined. The two relevant
snippets of code for 'call' and 'callvirt', including the setting of the
'this' argument to null, are as follows:

---8<---
// CALL
00DE0097 33C9 xor ecx,ecx
00DE0099 FF1520309100 call dword ptr ds:[00913020h]

// CALLVIRT
00DE00C6 33C9 xor ecx,ecx
00DE00C8 3909 cmp dword ptr [ecx],ecx
00DE00CA FF1520309100 call dword ptr ds:[00913020h]
--->8---

The difference with CALLVIRT is that it tests the pointer by
dereferencing it. That causes a hardware exception when the pointer is
null, and that hardware exception gets propagated to the CLR via Windows
SEH.

Something interesting that can be observed from this: the calls to the
App.Test() method are through an indirection. A peek in the address
shows the data:

---8<---
d -format:fourbytes 0x00913020

0x00913020 00de00e8 00de0070 00000080 022b1ec4
0x00913030 912fd8b8 e9ed8900 ffa2eed0 912fe0b8
0x00913040 e9ed8900 ffa2eec4 576f62e8 cccc5e79
0x00913050 00912fe0 00000000 00000000 00000000
--->8---

One can then disassemble the code at the indirect location:

---8<---
!u 0x00de00e8

Normal JIT generated code
App.Test()
Begin 00de00e8, size 44 00DE00E8 57 push edi


... etc.
--->8---

So, calls to non-virtual instance methods compiled with the current C#
compiler get turned into CIL 'callvirt' instructions, which, with the
current JIT compiler, test the 'this' argument with a CMP instruction.

-- Barry

--
http://barrkel.blogspot.com/

May 29 '06 #17

This discussion thread is closed

Replies have been disabled for this discussion.