Sta*_*erc 22 .net c++-cli visual-c++
最近我遇到了一些我应用程序的奇怪行为.它主要是在C#中开发的,但CLI/C++也用于实现更好的性能.我在TimeSpan比较中以一个非常简单的方法获得了System.NullReferenceException:
TimeSpan _timestamp;
void UpdateFrame(TimeSpan timestamp)
{
if(TimeSpan::Equals(_timestamp, timestamp) == false)
Run Code Online (Sandbox Code Playgroud)
很明显,这个表达式中使用的唯一引用是隐含的(this._timestamp).我添加了一个断言语句,结果证明这实际上是空的.经过短暂的调查,我设法准备了一个简短的程 它是C++/CLI.
using namespace System;
using namespace System::Reflection;
public class Unmanaged
{
public:
int value;
};
public ref class Managed
{
public:
int value;
Unmanaged* GetUnmanaged()
{
SampleMethod();
return new Unmanaged();
}
void SampleMethod()
{
System::Diagnostics::Debug::Assert(this != nullptr);
this->value = 0;
}
};
public ref class ManagedAccessor
{
public:
property Managed^ m;
};
int main(array<System::String ^> ^args)
{
ManagedAccessor^ ma = gcnew ManagedAccessor();
// Confirm that ma->m == null
System::Diagnostics::Debug::Assert(ma->m == nullptr);
// Invoke method on the null reference
delete ma->m->GetUnmanaged();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
有人知道怎么可能吗?这是编译器中的错误吗?
Gre*_*ill 19
在C++中(可能在C++/CLI中),没有什么可以阻止您尝试在NULL指针上调用方法.在大多数实现中,虚拟方法调用将在调用时崩溃,因为运行时将无法读取虚方法表.但是,非虚方法调用只是一个带有一些参数的函数调用,其中一个参数是this指针.如果它为null,那么这就是传递给函数的内容.
我相信在NULL(或nullptr)指针上调用任何成员函数的结果是正式的"未定义行为".
Sta*_*erc 12
谢谢Greg的回答,就像你描述的那样.但是,我对这种情况不满意,因为这意味着我必须放置
if(this == nullptr) throw gcnew ArgumentException("this");
Run Code Online (Sandbox Code Playgroud)
在每个方法的开头.只有这样才能保证我的方法不会在没有参数验证的情况下作为错误的代码段出现在堆栈跟踪的顶部.
当我用C#编写时,我从未遇到过(这= = null).因此,我决定了解它与C++/CLI有何不同.我在C++/CLI中创建了一个示例应用程序:
namespace ThisEqualsNull{
public ref class A
{
public:
void SampleMethod()
{
System::Diagnostics::Debug::Assert(this != nullptr);
}
};
public ref class Program{
public:
static void Main(array<System::String ^> ^args)
{
A^ a = nullptr;
a->SampleMethod();
}
};
}
Run Code Online (Sandbox Code Playgroud)
C#中的一个小程序,它使用具有相同Main方法的C++/CLI类:
class Program
{
static void Main(string[] args)
{
A a = null;
a.SampleMethod();
}
}
Run Code Online (Sandbox Code Playgroud)
然后我用Red Gate的.NET Reflector将它们拆开:
C++/CLI
.method public hidebysig static void Main(string[] args) cil managed
{
.maxstack 1
.locals ( [0] class ThisEqualsNull.A a)
L_0000: ldnull
L_0001: stloc.0
L_0002: ldnull
L_0003: stloc.0
L_0004: ldloc.0
L_0005: call instance void ThisEqualsNull.A::SampleMethod()
L_000a: ret
}
C#
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 1
.locals init ( [0] class [ThisEqualsNull]ThisEqualsNull.A a)
L_0000: nop
L_0001: ldnull
L_0002: stloc.0
L_0003: ldloc.0
L_0004: callvirt instance void [ThisEqualsNull]ThisEqualsNull.A::SampleMethod()
L_0009: nop
L_000a: ret
}
Run Code Online (Sandbox Code Playgroud)
重要的部分是:
C++/CLI
L_0005: call instance void ThisEqualsNull.A::SampleMethod()
C#
L_0004: callvirt instance void [ThisEqualsNull]ThisEqualsNull.A::SampleMethod()
Run Code Online (Sandbox Code Playgroud)
哪里:
现在最后的结论是: