C# 在设置为null之前检查是否为null?
在将变量设置为null之前,我们是否应该检查它是否为nullC# 在设置为null之前检查是否为null?,c#,.net,null,C#,.net,Null,在将变量设置为null之前,我们是否应该检查它是否为null if (MyBills != null) { MyBills = null; } 例如,在一个应用程序中,性能影响是最小的。 这是C语言的情况吗?其他影响 测试 我已经创建了以下代码进行测试: var watch = System.Diagnostics.Stopwatch.StartNew(); int iterations = int.MaxValue; List<int> myBills= null;
if (MyBills != null)
{
MyBills = null;
}
例如,在一个应用程序中,性能影响是最小的。
这是C语言的情况吗?其他影响
测试
我已经创建了以下代码进行测试:
var watch = System.Diagnostics.Stopwatch.StartNew();
int iterations = int.MaxValue;
List<int> myBills= null;
for (var i = 0; i < iterations; i++)
{
if (myBills!= null)
{
myBills = null;
}
}
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
Console.WriteLine(elapsedMs);
因此,即使在非受控环境中测试,性能影响也无关紧要。不,没有太多用处。可能检查变量是否为null与多次将其设置为null一样昂贵
如果它是一个属性,后面有额外的逻辑,那么在之前测试它是有意义的,但这实际上应该是属性中的逻辑的责任,而不是您的代码。除了property/setter参数之外,我在您提供的代码中看不到任何逻辑原因 但是,如果要在将对象设置为null之前对其执行额外操作,则有必要:
if (MyBills != null)
{
MyBills.Dispose();
MyBills = null;
}
或者执行一些其他操作,如记录结果
但这两个示例都不在您提供的示例代码中
因为人们似乎在质疑我的第一个例子的用例,这里有一个。这将防止多次调用Dispose导致ObjectDisposedException
我进行了一些计时,发现以下结果:
valueClass带有空检查=1361
带有空检查的nullClass=1173
valueClass不带空检查=1208
无空检查的nullClass=1148
正如您可以看到的,没有空检查,它会稍微快一点,但不足以进行任何显著的优化。此外,我在调试模式下从编译器执行了这些计时,并关闭了优化,因此它们不是100%准确/相关的
然而,当我在发布模式下运行时,在编译器外部启用了优化,结果非常接近,无论检查与否都可以忽略不计
以及测试代码:
using System;
namespace NullCheckTest
{
class Program
{
const int iterations = 10000000;
static void Main(string[] args)
{
MyClass valueClass = new MyClass() { Value = 10 };
MyClass nullClass = null;
Console.WriteLine($"valueClass with null check = {TestNullCheck(valueClass)}");
Console.WriteLine($"nullClass with null check = {TestNullCheck(nullClass)}");
Console.WriteLine($"valueClass with no null check = {TestNoNullCheck(valueClass)}");
Console.WriteLine($"nullClass with no null check = {TestNoNullCheck(nullClass)}");
Console.ReadLine();
}
static long TestNullCheck(MyClass myClass)
{
MyClass initial = myClass;
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
for (int i = 0; i < iterations; ++i)
{
sw.Start();
if (myClass != null)
{
myClass = null;
}
sw.Stop();
myClass = initial;
}
return sw.ElapsedMilliseconds;
}
static long TestNoNullCheck(MyClass myClass)
{
MyClass initial = myClass;
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
for (int i = 0; i < iterations; ++i)
{
sw.Start();
myClass = null;
sw.Stop();
myClass = initial;
}
return sw.ElapsedMilliseconds;
}
}
public class MyClass
{
public int Value { get; set; }
}
}
回答这个问题:不,没有必要 不过。作为一般的经验法则,对象变量和属性永远不应该为null,因为如果在某个地方设置了值,则必须始终检查代码 您应该做的是使用自定义类来指示对象变量或属性可能未初始化 像这样:
public class Maybe<T> where T : class
{
public readonly T Value { get; }
public Maybe(T value)
{
_Value = value;
}
public bool HasValue => _Value != null;
public bool HasNoValue => _Value == null;
public static operation Maybe<T>(T value) => new Maybe(value);
}
然后您可以这样使用它:
public class Foo
{
public Maybe<Bar> Bar { get; set; }
public int GetBarCount()
{
return Bar.HasValue ? Bar.Count : 0;
}
}
public class Bar
{
public int Count { get; set; }
}
上述代码适用于以下所有示例:
Foo foo = new Foo();
foo.Bar = new Bar(); // Outputs 0 when GetBarCount is called
foo.Bar = new Bar { Count = 2 }; // Outputs 2 when GetBarCount is called
foo.Bar = new Maybe<Bar>(new Bar()); // Outputs 0 when GetBarCount is called
foo.Bar = new Maybe<Bar>(new Bar { Count = 2 }); // Outputs 2 when GetBarCount is called
foo.Bar = new Maybe<Bar>(null); // Outputs 0 when GetBarCount is called
foo.Bar = null; // Outputs 0 when GetBarCount is called
注意当使用Maybe时,您可以显式地将Foo.Bar标记为不总是有值,并提供一个很强的信号。但是,如果不在它上标记,则表明它总是有值的。通过在正确的位置使用Maybe包装器,代码变得更容易处理,因为您不再需要对未包装的对象变量和属性执行空检查。当然不,为什么?您所称的变量可能是带有setter的属性,您不想运行哪种代码,然后您就可以执行此操作。我们是否应该检查。。。只有当你想处理这个案子的时候,f.e.logit@MegaTron确切地这就是问题所在。我会很感激你的评论。@MarioLevrero在你做了你的答案后,我用我的测试编辑了我的答案,所以这很好!我想在这个基础上保留它是值得的。谢谢,帕特里克。我不明白为什么会有这么多的反对者来回答这个问题。也许他们希望在提问时多付出一些努力,就像现在你链接到Java作为比较一样。你也可以做一些测试。帕特里克,我已经按照你的建议用一些测试更新了这个问题。我认为只有像你一样有建设性的批评,我们才能做出更好的S.O.再次感谢你是受欢迎的。事实上,它并不像你在测试中看到的那么重要。虽然有更好的测试方法,这将产生更准确的结果。好吧,您正在通过添加一些内容来修改OP的代码。这并不是对他的问题的真正回答。@Sinatr,这就是我的结束语。然而,它仍然是一个答案,因为它说明了这个问题的用例,而且手动调用Dispose的案例并不多。“使用在这方面更有用。@Patrickhoffman,除了在一个带有需要处理的字段的一次性类中。在我看来,这是一个常见的用例。如果一个类支持IDisposable,我将在调用时使用block来包装它。不需要设置为null。另外,如果它是一个类的实例,只需再次创建新实例。Like var mc=新的MyClass;为什么要这样混淆代码?另外,您仍然在检查null,在我看来,这个包装器类是混乱和毫无意义的。请参阅我在底部添加的注释。它应该解释得足够好。我理解你为什么这么做,我只是不明白它的意义。return Bar.HasValue有什么用?条数:0;超过返回条?计数??0;? 这造成了巨大的开销,却没有任何收益。恶心,恶心,恶心。
public class Foo
{
public Maybe<Bar> Bar { get; set; }
public int GetBarCount()
{
return Bar.HasValue ? Bar.Count : 0;
}
}
public class Bar
{
public int Count { get; set; }
}
Foo foo = new Foo();
foo.Bar = new Bar(); // Outputs 0 when GetBarCount is called
foo.Bar = new Bar { Count = 2 }; // Outputs 2 when GetBarCount is called
foo.Bar = new Maybe<Bar>(new Bar()); // Outputs 0 when GetBarCount is called
foo.Bar = new Maybe<Bar>(new Bar { Count = 2 }); // Outputs 2 when GetBarCount is called
foo.Bar = new Maybe<Bar>(null); // Outputs 0 when GetBarCount is called
foo.Bar = null; // Outputs 0 when GetBarCount is called