سؤال

كيف يمكنني التحقق مما إذا كان كائن معين لاغيًا وبعبارة أخرى كيفية تنفيذ الطريقة التالية ...

bool IsNullableValueType(object o)
{
    ...
}

يحرر:أنا أبحث عن أنواع القيمة الفارغة.لم يكن لدي أنواع المرجع في الاعتبار.

//Note: This is just a sample. The code has been simplified 
//to fit in a post.

public class BoolContainer
{
    bool? myBool = true;
}

var bc = new BoolContainer();

const BindingFlags bindingFlags = BindingFlags.Public
                        | BindingFlags.NonPublic
                        | BindingFlags.Instance
                        ;


object obj;
object o = (object)bc;

foreach (var fieldInfo in o.GetType().GetFields(bindingFlags))
{
    obj = (object)fieldInfo.GetValue(o);
}

يشير obj الآن إلى كائن من النوع bool (System.Boolean) بقيمة تساوي true.ما أردته حقًا هو كائن من النوع Nullable<bool>

والآن، كحل بديل، قررت التحقق مما إذا كان o لاغٍ وإنشاء غلاف لاغٍ حول obj.

هل كانت مفيدة؟

المحلول

وهناك نوعان من قيم الفارغة - Nullable<T> وإشارة من نوع

وقد صحح

وجون لي أنه من الصعب الحصول على نوع إذا محاصر، ولكن يمكنك مع الأدوية:  - فكيف أدناه. هذا هو في الواقع اختبار نوع T، ولكن باستخدام المعلمة obj لمجرد نوع عام الاستدلال (لتجعل من السهل للاتصال) - انها ستعمل بشكل مطابق تقريبا بدون المعلمة obj، على الرغم

static bool IsNullable<T>(T obj)
{
    if (obj == null) return true; // obvious
    Type type = typeof(T);
    if (!type.IsValueType) return true; // ref-type
    if (Nullable.GetUnderlyingType(type) != null) return true; // Nullable<T>
    return false; // value-type
}

ولكن هذا لن يعمل بشكل جيد إذا كان لديك بالفعل محاصر القيمة إلى متغير كائن.

نصائح أخرى

وهناك حل بسيط جدا باستخدام الزائدة طريقة

http://deanchalk.com/is-it-nullable/

ومقتطفات:

public static class ValueTypeHelper
{
    public static bool IsNullable<T>(T t) { return false; }
    public static bool IsNullable<T>(T? t) where T : struct { return true; }
}

ثم

static void Main(string[] args)
{
    int a = 123;
    int? b = null;
    object c = new object();
    object d = null;
    int? e = 456;
    var f = (int?)789;
    bool result1 = ValueTypeHelper.IsNullable(a); // false
    bool result2 = ValueTypeHelper.IsNullable(b); // true
    bool result3 = ValueTypeHelper.IsNullable(c); // false
    bool result4 = ValueTypeHelper.IsNullable(d); // false
    bool result5 = ValueTypeHelper.IsNullable(e); // true
    bool result6 = ValueTypeHelper.IsNullable(f); // true

ومسألة "كيف لمعرفة ما اذا كان نوع هو قيم الفارغة؟" هو في الواقع "كيفية معرفة ما إذا كان النوع هو Nullable<>؟"، والتي يمكن تعميمها على "كيفية معرفة ما إذا كان نوع هو نوع شيدت من نوع عام؟"، بحيث يجيب ليس فقط على السؤال "هل Nullable<int> على Nullable<> ؟ "، ولكن أيضا" هل List<int> على List<>؟ ".

ومعظم الحل المنصوص استخدام الأسلوب Nullable.GetUnderlyingType()، التي من الواضح أن تعمل فقط مع حالة Nullable<>. لم أكن أرى الحل عاكس العام التي ستعمل مع أي نوع عام، لذلك قررت أن إضافته هنا للأجيال القادمة، على الرغم من سبق أن أجبت على هذا السؤال منذ فترة طويلة.

لمعرفة ما اذا كان النوع هو شكل من أشكال Nullable<> باستخدام الانعكاس، عليك أولا أن تحويل نوع الخاصة بك عام شيدت، على سبيل المثال Nullable<int>، في تعريف نوع عام، Nullable<>. يمكنك القيام بذلك عن طريق استخدام طريقة GetGenericTypeDefinition() الطبقة Type. يمكنك ثم قارن نوع مما أدى إلى Nullable<>:

Type typeToTest = typeof(Nullable<int>);
bool isNullable = typeToTest.GetGenericTypeDefinition() == typeof(Nullable<>);
// isNullable == true

والشيء نفسه يمكن تطبيقه على أي نوع عام:

Type typeToTest = typeof(List<int>);
bool isList = typeToTest.GetGenericTypeDefinition() == typeof(List<>);
// isList == true

وهناك عدة أنواع قد يبدو نفسها، ولكن عدد مختلف من الحجج نوع يعني انها نوع مختلف تماما.

Type typeToTest = typeof(Action<DateTime, float>);
bool isAction1 = typeToTest.GetGenericTypeDefinition() == typeof(Action<>);
bool isAction2 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,>);
bool isAction3 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,,>);
// isAction1 == false
// isAction2 == true
// isAction3 == false

وبما أن مثيل كائن Type مرة واحدة لكل نوع، يمكنك التحقق من وجود المساواة إشارة بينهما. حتى إذا كنت ترغب في معرفة ما اذا كان كائنين من نفس تعريف نوع عام، يمكنك كتابة:

var listOfInts = new List<int>();
var listOfStrings = new List<string>();

bool areSameGenericType =
    listOfInts.GetType().GetGenericTypeDefinition() ==
    listOfStrings.GetType().GetGenericTypeDefinition();
// areSameGenericType == true

إذا كنت ترغب في معرفة ما اذا كان هو كائن قيم الفارغة، بدلا من Type، ثم يمكنك استخدام تقنية أعلاه جنبا إلى جنب مع حل مارك Gravell لخلق طريقة بسيطة إلى حد ما:

static bool IsNullable<T>(T obj)
{
    if (!typeof(T).IsGenericType)
        return false;

    return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>);
}

وهذا يعمل بالنسبة لي، ويبدو بسيط:

static bool IsNullable<T>(T obj)
{
    return default(T) == null;
}

حسنا، هل يمكن استخدام:

return !(o is ValueType);

و... ولكن كائن في حد ذاته ليس قيم الفارغة أو غير ذلك - و<م> نوع هو. كيف كنت تخطط لاستخدام هذا؟

وأبسط طريقة يمكنني معرفة هي:

public bool IsNullable(object obj)
{
    Type t = obj.GetType();
    return t.IsGenericType 
        && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}

وهناك نوعان من القضايا هنا: 1) اختبار لمعرفة ما إذا كان نوع هو قيم الفارغة. و 2) اختبار لمعرفة ما إذا كان كائن يمثل نوع قيم الفارغة.

لقضية 1 (اختبار النوع)، وهنا حل لقد استعملت في أنظمة بلدي: <لأ href = "https://stackoverflow.com/questions/108104/how-do-i-convert-a -system من نوع لله في قيم الفارغة إصدار / 7759487 # 7759487 "> حل TypeIsNullable الاختيار

لالعدد 2 (اختبار كائن)، حل عميد الطباشير في أعلاه يعمل لأنواع قيمة، لكنه لا يعمل لأنواع المرجعية، منذ باستخدام الزائد يعود دائما كاذبة. منذ أنواع المراجع هي قيم الفارغة أصلا، واختبار نوع مرجع يجب إرجاع صحيحا دائما. يرجى الاطلاع على مذكرة [من "nullability"] أدناه للحصول على شرح لهذه دلالات. وهكذا، وهنا بلدي تعديل نهج العميد:

    public static bool IsObjectNullable<T>(T obj)
    {
        // If the parameter-Type is a reference type, or if the parameter is null, then the object is always nullable
        if (!typeof(T).IsValueType || obj == null)
            return true;

        // Since the object passed is a ValueType, and it is not null, it cannot be a nullable object
        return false; 
    }

    public static bool IsObjectNullable<T>(T? obj) where T : struct
    {
        // Always return true, since the object-type passed is guaranteed by the compiler to always be nullable
        return true;
    }

وهنا بلدي تعديل رمز العميل اختبار للحل أعلاه:

    int a = 123;
    int? b = null;
    object c = new object();
    object d = null;
    int? e = 456;
    var f = (int?)789;
    string g = "something";

    bool isnullable = IsObjectNullable(a); // false 
    isnullable = IsObjectNullable(b); // true 
    isnullable = IsObjectNullable(c); // true 
    isnullable = IsObjectNullable(d); // true 
    isnullable = IsObjectNullable(e); // true 
    isnullable = IsObjectNullable(f); // true 
    isnullable = IsObjectNullable(g); // true

والسبب في أنني قمت بتعديل النهج العميد في IsObjectNullable (T ر) هو أن نهجه الأصلي عاد دائما كاذبة عن نوع مرجع. منذ أسلوبا مثل IsObjectNullable ينبغي أن تكون قادرة على التعامل مع القيم المرجعية من نوع ولأن كل أنواع المراجع هي قيم الفارغة أصلا، ثم إذا كان يتم تمرير أي نوع مرجع أو لاغية، وطريقة يجب أن تعود صحيحا دائما.

ويمكن الاستعاضة طريقتين المذكورة أعلاه مع أسلوب واحد التالية وتحقيق نفس الإخراج:

    public static bool IsObjectNullable<T>(T obj)
    {
        Type argType = typeof(T);
        if (!argType.IsValueType || obj == null)
            return true;
        return argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(Nullable<>);
    }

ولكن المشكلة مع هذا النهج الماضي،-طريقة واحدة هي أن أداء يعاني عند استخدام المعلمة قيم الفارغة . فإنه يأخذ المزيد من الوقت المعالج لتنفيذ السطر الأخير من هذه الطريقة واحد من يفعل للسماح للمترجم أن يختار الطريقة الثانية الزائد هو موضح سابقا عند استخدام قيم الفارغة نوع المعلمة في الدعوة IsObjectNullable. لذا، فإن الحل الأمثل هو استخدام نهج أسلوب اثنين يتضح هنا.

والتحذير: هذا الأسلوب يعمل بشكل موثوق إلا إذا دعت باستخدام مرجع كائن الأصلية أو صورة طبق الأصل، كما هو مبين في الأمثلة. ومع ذلك، إذا تم محاصر كائن nullable لنوع آخر (مثل كائن، وما إلى ذلك) بدلا من البقاء في شكله الأصلي قيم الفارغة <>، وهذه الطريقة لن تعمل بشكل موثوق. إذا كان رمز الدعوة هذا الأسلوب لا يستخدم الأصلية، مرجع كائن بدون علبة أو صورة طبق الأصل، فإنه لا يمكن تحديد موثوق nullability الكائن باستخدام هذا الأسلوب.

في معظم الحالات الترميز، لتحديد nullability لا بد من الاعتماد بدلا من ذلك على اختبار نوع الكائن الأصلي، وليس مرجعها (على سبيل المثال، يجب أن يكون رمز الوصول إلى الكائن النوع الأصلي لتحديد nullability). في هذه الحالات أكثر شيوعا، IsTypeNullable (انظر الرابط) هو وسيلة يمكن الاعتماد عليها لتحديد nullability.

وP.S. - معلومات عن "nullability"

وأود أن أكرر بيان حول nullability الذي أدليت به في وظيفة مستقلة، وهو ما ينطبق مباشرة على معالجة هذا الموضوع بشكل صحيح. وهذا هو، وأعتقد أن التركيز في النقاش هنا لا ينبغي أن يكون كيفية تحقق لمعرفة ما إذا كان الهدف من ذلك هو نوع قيم الفارغة عام، بل ما إذا كان يمكن تعيين قيمة لاغية لكائن من نوعه. وبعبارة أخرى، أعتقد أننا يجب تحديد ما إذا كان نوع الكائن قيم الفارغة، وليس ما إذا كان من قيم الفارغة. والفرق هو في دلالات، وهي أسباب عملية لتحديد nullability، التي عادة ما تكون كل ما يهم.

في نظام باستخدام الكائنات مع أنواع ربما غير معروفة حتى وقت التشغيل (خدمات شبكة الإنترنت، والمكالمات البعيدة، وقواعد البيانات، إس، الخ)، وهو مطلب مشترك هو تحديد ما إذا كان يمكن أن تسند فارغة إلى كائن، أو ما إذا كان قد تحتوي على وجوه فارغة. سوف أداء هذه العمليات على أنواع غير المرجح قيم الفارغة إنتاج الأخطاء، وعادة الاستثناءات، والتي هي مكلفة جدا سواء من حيث الأداء ومتطلبات الترميز. إلى اتخاذ نهج درجة عالية من المفضل تجنب مثل هذه المشاكل بشكل استباقي، فمن الضروري تحديد ما إذا كان كائن من arbitrary نوع قادر على احتواء فارغة؛ أي، هل هو عموما "قيم الفارغة.

في الشعور عملي جدا ونموذجية، nullability من حيث .NET لا على الإطلاق بالضرورة يعني أن نوع الكائن هو شكل من أشكال قيم الفارغة. في كثير من الحالات في الواقع، وكائنات لها أنواع المراجع، يمكن أن تحتوي على قيمة فارغة، وبالتالي كلها قيم الفارغة. أيا من هذه يكون لها نوع قيم الفارغة. لذلك، لأغراض عملية في معظم الحالات، ينبغي أن يتم اختبار المفهوم العام للnullability، مقابل مفهوم تعتمد على تنفيذ قيم الفارغة. لذلك نحن لا ينبغي التعلق من خلال التركيز فقط على نوع صافي قيم الفارغة بل تتضمن فهمنا لمتطلباته والسلوك في عملية التركيز على، مفهوم عملي العام للnullability.

وأبسط حل خطرت لي هو تنفيذ حل مايكروسوفت (<لأ href = "https://msdn.microsoft.com/en-us/library/ms366789.aspx؟f=255&MSPPError=-2147217396" يختلط = "نوفولو noreferrer"> كيفية: تحديد نوع قيم الفارغة (C # دليل البرمجة) ) كطريقة التمديد:

public static bool IsNullable(this Type type)
{
    return Nullable.GetUnderlyingType(type) != null;
}

وهذا يمكن بعد ذلك أن يسمى مثل ذلك:

bool isNullable = typeof(int).IsNullable();

وهذا يبدو أيضا وسيلة منطقية للوصول IsNullable() لتناسبها مع كافة الأساليب IsXxxx() أخرى من الطبقة Type.

ويكون متأن، عندما الملاكمة نوع قيم الفارغة (Nullable<int> أو كثافة العمليات على سبيل المثال؟):

int? nullValue = null;
object boxedNullValue = (object)nullValue;
Debug.Assert(boxedNullValue == null);

int? value = 10;
object boxedValue = (object)value;
Debug.Assert( boxedValue.GetType() == typeof(int))

ويصبح من نوع مرجع صحيح، لذلك تفقد الواقع كان قيم الفارغة.

ربما خارج الموضوع قليلا، ولكن لا تزال هناك بعض المعلومات المثيرة للاهتمام.أجد الكثير من الأشخاص الذين يستخدمون Nullable.GetUnderlyingType() != null إلى الهوية إذا كان النوع لاغيًا.من الواضح أن هذا يعمل، لكن مايكروسوفت تنصح بما يلي type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) (يرى http://msdn.microsoft.com/en-us/library/ms366789.aspx).

نظرت إلى هذا من جانب الأداء.نتيجة الاختبار (مليون محاولة) أدناه هي أنه عندما يكون النوع خاليًا، فإن خيار Microsoft يقدم أفضل أداء.

Nullable.GetUnderlyingType(): 1335 مللي ثانية (3 مرات أبطأ)

GetGenericTypeDefinition() == typeof(Nullable<>): 500 مللي ثانية

أعلم أننا نتحدث عن مقدار صغير من الوقت، لكن الجميع يحب تعديل المللي ثانية :-)!لذا، إذا كان رئيسك يريد منك تقليل بعض المللي ثانية، فهذا هو منقذك...

/// <summary>Method for testing the performance of several options to determine if a type is     nullable</summary>
[TestMethod]
public void IdentityNullablePerformanceTest()
{
    int attempts = 1000000;

    Type nullableType = typeof(Nullable<int>);

    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
    {
        Assert.IsTrue(Nullable.GetUnderlyingType(nullableType) != null, "Expected to be a nullable"); 
    }

    Console.WriteLine("Nullable.GetUnderlyingType(): {0} ms", stopwatch.ElapsedMilliseconds);

    stopwatch.Restart();

    for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
   {
       Assert.IsTrue(nullableType.IsGenericType && nullableType.GetGenericTypeDefinition() == typeof(Nullable<>), "Expected to be a nullable");
   }

   Console.WriteLine("GetGenericTypeDefinition() == typeof(Nullable<>): {0} ms", stopwatch.ElapsedMilliseconds);
   stopwatch.Stop();
}

هذه النسخة:

  • نتائج التخزين المؤقت أسرع،
  • لا يتطلب متغيرات غير ضرورية، مثل Method(T obj)
  • ليست معقدة :)،
  • مجرد فئة عامة ثابتة تحتوي على حقول محسوبة مرة واحدة

:

public static class IsNullable<T>
{
    private static readonly Type type = typeof(T);
    private static readonly bool is_nullable = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
    public static bool Result { get { return is_nullable; } }
}

bool is_nullable = IsNullable<int?>.Result;

إليكم ما توصلت إليه، حيث بدا أن كل شيء آخر قد فشل - على الأقل في PLC - مكتبة الفصول المحمولة / صافي النواة مع >= C#6

حل: توسيع الطرق الثابتة لأي نوع T و Nullable<T> واستخدم حقيقة أنه سيتم استدعاء طريقة الامتداد الثابتة التي تطابق النوع الأساسي وستكون لها الأسبقية على النوع العام T طريقة التمديد.

ل T:

public static partial class ObjectExtension
{
    public static bool IsNullable<T>(this T self)
    {
        return false;
    }
}

ولل Nullable<T>

public static partial class NullableExtension
{
    public static bool IsNullable<T>(this Nullable<T> self) where T : struct
    {
        return true;
    }
}

باستخدام الانعكاس و type.IsGenericType...لم يعمل على مجموعتي الحالية من أوقات تشغيل .NET.ولا وثائق MSDN يساعد.

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}

ويرجع ذلك جزئيًا إلى أن واجهة برمجة التطبيقات Reflection API قد تغيرت بشكل كبير في .NET Core.

وأعتقد أن تلك التجارب باستخدام مايكروسوفت اقترح ضد IsGenericType جيدون، ولكن في رمز لGetUnderlyingType، يستخدم Microsoft اختبار إضافي للتأكد من أنك لم يمر في عام Nullable<> نوع تعريف:

 public static bool IsNullableType(this Type nullableType) =>
    // instantiated generic type only                
    nullableType.IsGenericType &&
    !nullableType.IsGenericTypeDefinition &&
    Object.ReferenceEquals(nullableType.GetGenericTypeDefinition(), typeof(Nullable<>));

وطريقة بسيطة للقيام بذلك:

    public static bool IsNullable(this Type type)
    {
        if (type.IsValueType) return Activator.CreateInstance(type) == null;

        return true;
    }

وهذه هي بلدي وحدة الاختبارات وجميع مرت

    IsNullable_String_ShouldReturn_True
    IsNullable_Boolean_ShouldReturn_False
    IsNullable_Enum_ShouldReturn_Fasle
    IsNullable_Nullable_ShouldReturn_True
    IsNullable_Class_ShouldReturn_True
    IsNullable_Decimal_ShouldReturn_False
    IsNullable_Byte_ShouldReturn_False
    IsNullable_KeyValuePair_ShouldReturn_False

وحدة الفعلية الاختبارات

    [TestMethod]
    public void IsNullable_String_ShouldReturn_True()
    {
        var typ = typeof(string);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Boolean_ShouldReturn_False()
    {
        var typ = typeof(bool);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Enum_ShouldReturn_Fasle()
    {
        var typ = typeof(System.GenericUriParserOptions);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Nullable_ShouldReturn_True()
    {
        var typ = typeof(Nullable<bool>);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Class_ShouldReturn_True()
    {
        var typ = typeof(TestPerson);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Decimal_ShouldReturn_False()
    {
        var typ = typeof(decimal);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Byte_ShouldReturn_False()
    {
        var typ = typeof(byte);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_KeyValuePair_ShouldReturn_False()
    {
        var typ = typeof(KeyValuePair<string, string>);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top