我只是在看一个例子,在里面我看到了代码
return new IntPtr(handle);
在浏览了我们的代码之后,我发现我们已经使用了类似的模式,但是在我们的代码中,我们几乎有相同的东西:
return (IntPtr)handle;
这两种取法有区别吗?第二个是否会以任何方式“更好”,因为它不分配新内存,或者演员只是将相同的构造函数隐藏在下面?
在你的例子中,我猜句柄是一个整数值?IntPtr 声明了从 Int32 (int) 和 Int64 (long) 的显式转换,它简单地调用相同的构造函数:
public static explicit operator IntPtr(int value)
{
return new IntPtr(value);
}
因此,除了可能的可读性问题之外,实际上没有任何区别。
Reflector说演员无论如何都在调用构造函数:
[Serializable, StructLayout(LayoutKind.Sequential), ComVisible(true)]
public struct IntPtr : ISerializable
{
...
[ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
public static explicit operator IntPtr(int value)
{
return new IntPtr(value);
}
}
所以这个线程都是谈话,没有数字,所以让我们谈谈指标。我使用 Visual Studio 2010 运行了一些测试代码以获取一些性能指标
我通过计算任何一种方法在 10 次测试运行中的平均时间得到这些指标,每个测试运行 1000 万次迭代,然后在 Debug 然后 Release 模式(非优化然后优化):
(调试)铸造方法:~32 ms 分配方法:~26 ms
(发布)铸造方法:~20 ms 分配方法:~22 ms
同样有趣的是,仅使用 gcnew 将这些指标与托管 C++ 的类似代码进行比较,结果大不相同。
再次相同的设置。除了比较铸造方法:“IntPtr^ ptr = (IntPtr) i;” vs分配方法:“IntPtr^ ptr = (IntPtr) i;”。
(调试)铸造方法:~91ms 分配方法:~127ms
(发布)铸造方法:~22ms 分配方法:~124ms
现在,如果你挠头说为什么 C# 比托管 C++ 快得多,答案是它不是。使用 IntPtr 最有效的方法是作为值类型而不是对值类型的引用。例如像这样“IntPtr ptr = (IntPtr) i;”。这会给你〜24ms(调试更多)或(〜22发布模式)。看看编译器如何在上面优化它以获得 22 毫秒而不是 90 毫秒。
C# 中的结论,除非您正在查看非常非常紧凑的代码,否则没关系。我认为我在 Release 中的代码实际上是在优化演员阵容,因为注释掉演员阵容给出了相同的 ~22 毫秒。但在大多数情况下,编译器在 C# 中支持这一点,至少 VS 2010 支持。但是,在托管 C++/CLI 中,如果您正在查看具有最小性能限制的代码,那么请注意。编译器不会自动优化对转换方法的 gcnew 分配,它几乎快 6 倍......我实际上在 C++/CLI 中遇到了这个特殊问题,这就是我在处理一些实时音频时在这个线程上发布的原因加工。我的代码(C#):(我的托管 C++ 代码非常相似,除了我必须自己编写 Average() 并使用控制台输出而不是消息框)。
static void Main()
{
List<int> castTimings = new List<int>();
List<int> allocTimings = new List<int>();
for (int i = 0; i < TEST_RUNS; ++i)
{
castTimings.Add(RunCastMethod().Milliseconds);
allocTimings.Add(RunAllocationMethod().Milliseconds);
}
MessageBox.Show(string.Format("Casting Method took: {0}ms", castTimings.Average() ));
MessageBox.Show(string.Format("Allocation Method took: {0}ms", allocTimings.Average() ));
}
private static TimeSpan RunAllocationMethod() {
DateTime start = DateTime.Now;
for (int i = 0; i < TEST_ITERATIONS; ++i)
{
IntPtr ptr = new IntPtr(i);
}
return ( DateTime.Now - start );
}
private static TimeSpan RunCastMethod()
{
DateTime start = DateTime.Now;
for (int i = 0; i < TEST_ITERATIONS; ++i)
{
IntPtr ptr = (IntPtr) i;
}
return (DateTime.Now - start);
}
由于IntPtr
是值类型,因此 usingnew
不会分配任何内存。
从技术上讲,调用仍然编译为不同的 IL - 一个实际调用构造函数,另一个调用显式转换运算符。但是,我不确定在通过 JIT 之后这两者之间是否有任何实际差异 - 很可能没有(尽管我怀疑您在实践中会注意到任何一种方式,这是一种毫微微优化)。
在任何情况下,强制转换比使用构造函数更惯用,所以我建议仅出于这个原因就使用它。