سؤال

اعتدت دائما Nullable<>.HasValue لأنني أحببت الدلالات.ومع ذلك، كنت أعمل مؤخرًا على قاعدة التعليمات البرمجية الموجودة لشخص آخر حيث استخدموها Nullable<> != null بدلا من ذلك حصرا.

فهل هناك سبب لاستخدام أحدهما على الآخر أم أنه مجرد تفضيل؟

  1. int? a;
    if (a.HasValue)
        // ...
    

ضد.

  1. int? b;
    if (b != null)
        // ...
    
هل كانت مفيدة؟

المحلول

يستبدل المترجم المقارنات الفارغة باستدعاء HasValue, ، لذلك لا يوجد فرق حقيقي.ما عليك سوى القيام بما هو أكثر قابلية للقراءة/أكثر منطقية لك ولزملائك.

نصائح أخرى

أنا أفضل (a != null) بحيث يتطابق بناء الجملة مع أنواع المراجع.

لقد أجريت بعض الأبحاث حول هذا باستخدام طرق مختلفة لتعيين قيم لقيمة لاغية.هذا ما حدث عندما فعلت أشياء مختلفة.ينبغي توضيح ما يحدث.تذكر: Nullable<something> أو الاختصار something? هي بنية يبدو أن المترجم يقوم بالكثير من العمل للسماح لنا باستخدامها مع القيمة الفارغة كما لو كانت فئة.
كما سترى أدناه، SomeNullable == null و SomeNullable.HasValue سيُرجع دائمًا صوابًا أو خطأً متوقعًا.على الرغم من عدم إظهاره أدناه، SomeNullable == 3 صالح أيضًا (بافتراض أن SomeNullable هو ملف int?).
بينما SomeNullable.Value يحصل لنا خطأ في وقت التشغيل إذا قمنا بتعيينه null ل SomeNullable.هذه في الواقع هي الحالة الوحيدة التي يمكن أن تسبب لنا فيها القيم الفارغة مشكلة، وذلك بفضل مجموعة من المشغلين المثقلين، المحملين بشكل زائد object.Equals(obj) الطريقة، وتحسين المترجم والأعمال القردية.

فيما يلي وصف لبعض التعليمات البرمجية التي قمت بتشغيلها، وما هو الناتج الذي تم إنتاجه في الملصقات:

int? val = null;
lbl_Val.Text = val.ToString(); //Produced an empty string.
lbl_ValVal.Text = val.Value.ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValEqNull.Text = (val == null).ToString(); //Produced "True" (without the quotes)
lbl_ValNEqNull.Text = (val != null).ToString(); //Produced "False"
lbl_ValHasVal.Text = val.HasValue.ToString(); //Produced "False"
lbl_NValHasVal.Text = (!(val.HasValue)).ToString(); //Produced "True"
lbl_ValValEqNull.Text = (val.Value == null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValValNEqNull.Text = (val.Value != null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")

حسنًا، لنجرب طريقة التهيئة التالية:

int? val = new int?();
lbl_Val.Text = val.ToString(); //Produced an empty string.
lbl_ValVal.Text = val.Value.ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValEqNull.Text = (val == null).ToString(); //Produced "True" (without the quotes)
lbl_ValNEqNull.Text = (val != null).ToString(); //Produced "False"
lbl_ValHasVal.Text = val.HasValue.ToString(); //Produced "False"
lbl_NValHasVal.Text = (!(val.HasValue)).ToString(); //Produced "True"
lbl_ValValEqNull.Text = (val.Value == null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValValNEqNull.Text = (val.Value != null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")

كل نفس كما كان من قبل.ضع في اعتبارك أن التهيئة باستخدام int? val = new int?(null);, ، مع تمرير قيمة null إلى المُنشئ، كان من الممكن أن ينتج خطأ وقت الترجمة، نظرًا لأن قيمة الكائن الفارغة ليست فارغة.إن كائن الغلاف نفسه فقط هو الذي يمكن أن يساوي قيمة فارغة.

وبالمثل، سوف نحصل على خطأ وقت الترجمة من:

int? val = new int?();
val.Value = null;

ناهيك عن ذلك val.Value هي خاصية للقراءة فقط على أي حال، مما يعني أنه لا يمكننا حتى استخدام شيء مثل:

val.Value = 3;

ولكن مرة أخرى، تتيح لنا عوامل التحويل الضمنية المتعددة الأشكال والمحملة بشكل زائد القيام بما يلي:

val = 3;

لا داعي للقلق بشأن أي شيء متعدد الأشياء، طالما أنه يعمل بشكل صحيح؟:)

في برنامج VB.Net.لا تستخدم "IsNot Nothing" عندما يمكنك استخدام ".HasValue".لقد قمت للتو بحل خطأ الثقة المتوسطة "قد تؤدي العملية إلى زعزعة استقرار وقت التشغيل" عن طريق استبدال "ليس لا شيء" بـ ".HasValue" في مكان واحد.لا أفهم حقًا السبب، ولكن شيئًا ما يحدث بشكل مختلف في المترجم.أفترض أن "! = null" في C# قد يكون له نفس المشكلة.

إذا كنت تستخدم linq وتريد أن تبقي الكود الخاص بك قصيرًا، فإنني أنصحك باستخدامه دائمًا !=null

ولهذا السبب:

دعونا نتخيل أن لدينا فئة ما Foo مع مزدوج لاغية عامل SomeDouble

public class Foo
{
    public double? SomeDouble;
    //some other properties
}   

إذا أردنا في مكان ما في الكود الخاص بنا الحصول على جميع Foo بقيم SomeDouble غير فارغة من مجموعة Foo (بافتراض أن بعض foos في المجموعة يمكن أن تكون فارغة أيضًا)، فسينتهي بنا الأمر بثلاث طرق على الأقل لكتابة وظيفتنا (إذا كنا استخدم C # 6):

public IEnumerable<Foo> GetNonNullFoosWithSomeDoubleValues(IEnumerable<Foo> foos)
{
     return foos.Where(foo => foo?.SomeDouble != null);
     return foos.Where(foo=>foo?.SomeDouble.HasValue); // compile time error
     return foos.Where(foo=>foo?.SomeDouble.HasValue == true); 
     return foos.Where(foo=>foo != null && foo.SomeDouble.HasValue); //if we don't use C#6
}

وفي هذا النوع من المواقف أوصي دائمًا بالاختيار الأقصر

الإجابة العامة والقاعدة الأساسية:إذا كان لديك خيار (على سبيل المثال.كتابة مُسلسلات مخصصة) لمعالجة Nullable في خط أنابيب مختلف عن object - واستخدم خصائصها المحددة - افعل ذلك واستخدم خصائص Nullable المحددة.لذلك من وجهة نظر التفكير المتسقة HasValue ينبغي أن يكون المفضل.يمكن أن يساعدك التفكير المتسق على كتابة تعليمات برمجية أفضل دون قضاء الكثير من الوقت في التفاصيل.على سبيل المثالستكون هناك طريقة ثانية أكثر فعالية عدة مرات (غالبًا بسبب تضمين المترجمين والملاكمة ولكن لا تزال الأرقام معبرة جدًا):

public static bool CheckObjectImpl(object o)
{
    return o != null;
}

public static bool CheckNullableImpl<T>(T? o) where T: struct
{
    return o.HasValue;
}

اختبار المعيار:

BenchmarkDotNet=v0.10.5, OS=Windows 10.0.14393
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233539 Hz, Resolution=309.2587 ns, Timer=TSC
  [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1648.0
  Clr    : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1648.0
  Core   : .NET Core 4.6.25009.03, 64bit RyuJIT


        Method |  Job | Runtime |       Mean |     Error |    StdDev |        Min |        Max |     Median | Rank |  Gen 0 | Allocated |
-------------- |----- |-------- |-----------:|----------:|----------:|-----------:|-----------:|-----------:|-----:|-------:|----------:|
   CheckObject |  Clr |     Clr | 80.6416 ns | 1.1983 ns | 1.0622 ns | 79.5528 ns | 83.0417 ns | 80.1797 ns |    3 | 0.0060 |      24 B |
 CheckNullable |  Clr |     Clr |  0.0029 ns | 0.0088 ns | 0.0082 ns |  0.0000 ns |  0.0315 ns |  0.0000 ns |    1 |      - |       0 B |
   CheckObject | Core |    Core | 77.2614 ns | 0.5703 ns | 0.4763 ns | 76.4205 ns | 77.9400 ns | 77.3586 ns |    2 | 0.0060 |      24 B |
 CheckNullable | Core |    Core |  0.0007 ns | 0.0021 ns | 0.0016 ns |  0.0000 ns |  0.0054 ns |  0.0000 ns |    1 |      - |       0 B |

رمز المعيار:

public class BenchmarkNullableCheck
{
    static int? x = (new Random()).Next();

    public static bool CheckObjectImpl(object o)
    {
        return o != null;
    }

    public static bool CheckNullableImpl<T>(T? o) where T: struct
    {
        return o.HasValue;
    }

    [Benchmark]
    public bool CheckObject()
    {
        return CheckObjectImpl(x);
    }

    [Benchmark]
    public bool CheckNullable()
    {
        return CheckNullableImpl(x);
    }
}

https://github.com/dotnet/BenchmarkDotNet كان مستعملا

ملاحظة.يقول الناس أن النصيحة "تفضل HasValue بسبب التفكير المستمر" ليست ذات صلة وغير مجدية. هل يمكنك التنبؤ بأداء هذا؟

public static bool CheckNullableGenericImpl<T>(T? t) where T: struct
{
    return t != null;
}

بس يستمر الناس ناقصًا ولكن لا أحد يحاول التنبؤ بأداء CheckNullableGenericImpl.وهناك مترجم لن يساعدك على الاستبدال !=null مع HasValue. HasValue يجب استخدامه مباشرة إذا كنت مهتمًا بالأداء.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top