条件与内存分配的C#性能

Ser*_*tch -1 c# performance conditional memory-management .net-core

考虑以下C#代码:

IntPtr native = GetNativeError(/* parameters here */);
return new ManagedError(native);
Run Code Online (Sandbox Code Playgroud)

然后客户端代码以下列方式检查错误:

ManagedError err = /* get it with the code above */;
if(err.IsOk()) {
    /* Success */
}
Run Code Online (Sandbox Code Playgroud)

这里的内存分配可以以if语句为代价来保存:

IntPtr native = GetNativeError(/* parameters here */);
if(native == IntPtr.Zero) {
    return null;
} else {
    return new ManagedError(native);
}
Run Code Online (Sandbox Code Playgroud)

然后客户端代码将检查错误,如下所示:

ManagedError err = /* get it with the code above */;
if(err == null) {
    /* Success */
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:哪种方法更快?第一个有额外的内存分配.第二个是附加if声明.

更新:我的意思是,在成功案例中.错误情况很少发生,可以慢.

Eri*_*ert 7

首先,你已经两种方式编写了代码.如果你想知道哪种方式有更好的性能运行两个程序并测量哪一个具有更好的性能.这是准确回答性能问题的唯一方法,所以请拿出秒表.

如果您无法测量哪一个具有更好的性能,那么显然哪个具有更好的性能无关紧要,因为无法检测到差异.不可观察的差异是无关紧要的.

现在让我们考虑一下您的具体问题.假设您决定在常见的成功案例中保存内存分配. 正确的解决方案不是给null特殊的意义.正确的解决方案是使用null对象模式.那就是创建一个对象的特殊实例,它总是在你使用null的地方使用.

class ManagedError 
{
  public static readonly Success = new ManagedError(IntPtr.Zero);
  private ManagedError(IntPtr hr) { ... }
  public ManagedError FromNative(IntPtr hr) 
  {
    if (hr == IntPtr.Zero) return Success;
    return new ManagedError(hr);
  }
}
...
IntPtr native = GetNativeError(/* parameters here */);
return ManagedError.FromNative(native);
Run Code Online (Sandbox Code Playgroud)

完成.您始终获得有效对象,并且在常见情况下不进行任何分配.

另外,正如另一个答案中所提到的:为什么这不是结构?你为什么要做任何引用类型堆内存分配?如果事物只是一个intptr的包装器那么它应该是一个结构; 它会像intptr一样便宜!

如果你使它成为一个结构,那么这变得更容易,因为你只需使用结构的默认实例作为空对象!

struct ManagedError 
{
  public static readonly Success = default(ManagedError);
  private readonly IntPtr hr;
  public ManagedError(IntPtr hr) { this.hr = hr }
}
Run Code Online (Sandbox Code Playgroud)

而且你已经完成了; 根本没有堆分配.你只需要整理intptr.