web-dev-qa-db-ja.com

オブジェクトがヌル可能かどうかを確認する方法は?

特定のオブジェクトがヌル可能かどうか、つまり次のメソッドを実装する方法を確認するにはどうすればよいですか?.

bool IsNullableValueType(object o)
{
    ...
}

編集:私はnull可能な値型を探しています。 ref型を念頭に置いていませんでした。

//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に等しいtrueSystem.Boolean)型のオブジェクトを参照するようになりました。私が本当に欲しかったのはタイプNullable<bool>のオブジェクトでした

そこで、回避策として、oがNULL可能かどうかを確認し、objのNULL可能ラッパーを作成することにしました。

188
Autodidact

Nullableには、Nullable<T>とreference-typeの2つのタイプがあります。

Jonは、ボックス化するとタイプを取得するのが難しいことを修正しましたが、ジェネリックを使用するとできます。これは実際にタイプ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
}

しかし、すでにオブジェクト変数に値をボックス化した場合、これはあまりうまくいきません。

252
Marc Gravell

メソッドのオーバーロードを使用した非常に簡単なソリューションがあります

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
47
Dean Chalk

「型がnull値を許可するかどうかを確認する方法」の質問実際には「型がNullable<>であるかどうかを確認する方法」であり、「型が何らかのジェネリック型の構築型であるかどうかを確認する方法」に一般化できるため、「 Nullable<int>Nullable<>?ですが、「List<int>List<>ですか?」でもあります。

提供されるソリューションのほとんどはNullable.GetUnderlyingType()メソッドを使用しますが、これは明らかにNullable<>の場合にのみ機能します。一般的なタイプで機能する一般的なリフレクションソリューションが見つからなかったため、この質問はすでに回答済みですが、後世のためにここに追加することにしました。

型がリフレクションを使用してNullable<>の形式であるかどうかを確認するには、まず、構築されたジェネリック型、たとえばNullable<int>をジェネリック型定義Nullable<>に変換する必要があります。それには、TypeクラスのGetGenericTypeDefinition()メソッドを使用します。次に、結果の型を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オブジェクトは型ごとに1回インスタンス化されるため、それらの間の参照の等価性を確認できます。したがって、2つのオブジェクトが同じジェネリック型定義であるかどうかを確認する場合は、次のように記述できます。

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

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

オブジェクトがTypeではなくnullableかどうかを確認したい場合は、上記の手法とMarc Gravellのソリューションを使用して、かなり単純なメソッドを作成できます。

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

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

これは私にとってはうまくいき、簡単に思えます:

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

さて、あなたは使用することができます:

return !(o is ValueType);

...しかし、オブジェクト自体はnullを許可しないか、そうでない場合はtypeですこれをどのように使用する予定でしたか?

20
Jon Skeet

私が理解できる最も簡単な方法は次のとおりです。

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

ここには2つの問題があります。1)Typeがnull可能かどうかをテストします。 2)オブジェクトがnull許容型を表すかどうかをテストします。

問題1(タイプのテスト)の場合、自分のシステムで使用したソリューションは次のとおりです。 TypeIsNullable-check solution

問題2(オブジェクトのテスト)の場合、上記のDean Chalkのソリューションは値型では機能しますが、<T>オーバーロードを使用すると常にfalseが返されるため、参照型では機能しません。参照型は本質的にNULL値を許可するため、参照型をテストすると常にtrueが返されます。これらのセマンティクスの説明については、下記の「[nullability」について」の注意を参照してください。したがって、Deanのアプローチに対する私の変更を次に示します。

    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>(T t)でDeanのアプローチを変更した理由は、元のアプローチが参照型に対して常にfalseを返したためです。 IsObjectNullableなどのメソッドは参照型の値を処理できる必要があり、すべての参照型は本質的にnull可能なので、参照型またはnullが渡された場合、メソッドは常にtrueを返す必要があります。

上記の2つの方法は、次の単一の方法に置き換えることができ、同じ出力を実現できます。

    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<>);
    }

ただし、この最後の単一メソッドアプローチの問題は、Nullable <T>パラメーターを使用するとパフォーマンスが低下することです。 IsObjectNullable呼び出しでNullable <T>型パラメーターが使用されている場合、コンパイラーが以前に示した2番目のメソッドのオーバーロードを選択できるようにするよりも、この単一メソッドの最後の行を実行するのにはるかに多くのプロセッサー時間がかかります。したがって、最適な解決策は、ここに示す2つの方法のアプローチを使用することです。

警告:このメソッドは、例に示すように、元のオブジェクト参照または正確なコピーを使用して呼び出された場合にのみ確実に機能します。ただし、nullableオブジェクトが元のNullable <>形式のままではなく、別のType(オブジェクトなど)にボックス化されている場合、このメソッドは確実に機能しません。このメソッドを呼び出すコードが、元のボックス化されていないオブジェクト参照または正確なコピーを使用していない場合、このメソッドを使用してオブジェクトのnull可能性を確実に判断できません。

ほとんどのコーディングシナリオでは、null可能性を判断するために、参照ではなく元のオブジェクトのTypeのテストに依存する必要があります(たとえば、null可能性を判断するには、コードがオブジェクトの元のTypeにアクセスできる必要があります)。これらのより一般的なケースでは、IsTypeNullable(リンクを参照)はnull可能性を判断する信頼できる方法です。

追伸-「nullability」について

このトピックに適切に対処するために直接適用される別の投稿で作成したnullabilityについてのステートメントを繰り返す必要があります。つまり、ここでの議論の焦点は、オブジェクトが汎用のNullable型であるかどうかを確認する方法ではなく、その型のオブジェクトにnullの値を割り当てることができるかどうかにあるべきだと思います。つまり、オブジェクト型がNullableであるかどうかではなく、Nullableであるかどうかを判断する必要があると思います。違いはセマンティクス、つまりヌル可能性を決定する実際的な理由にあります。

実行時まで不明なタイプのオブジェクト(Webサービス、リモート呼び出し、データベース、フィードなど)を使用するシステムでは、一般的な要件は、nullをオブジェクトに割り当てることができるかどうか、またはオブジェクトにnull。 nullを許可しない型でこのような操作を実行すると、通常は例外が発生し、パフォーマンスとコーディング要件の両方の点で非常にコストがかかります。このような問題を事前に回避するという非常に好ましいアプローチをとるには、任意のタイプのオブジェクトがnullを含むことができるかどうかを判断する必要があります。すなわち、それが一般に「nullable」であるかどうか。

非常に実用的で典型的な意味で、.NET用語のヌル可能性は、オブジェクトのタイプがNullableの形式であることを必ずしも意味しません。実際、多くの場合、オブジェクトには参照型があり、null値を含むことができるため、すべてnull可能です。これらはどれもNullable型を持ちません。したがって、ほとんどのシナリオでの実用的な目的のために、Nullableの実装依存の概念に対して、nullabilityの一般的な概念に対してテストを行う必要があります。したがって、.NET Nullable型のみに焦点を絞ることにとどまるべきではなく、その要件と動作に関する理解を、NULL可能性の一般的で実用的な概念に焦点を当てるプロセスに組み込む必要があります。

10
Mark Jones

私が思いついた最も簡単な解決策は、Microsoftのソリューション( 方法:Nullable型を識別する(C#プログラミングガイド) )を拡張メソッドとして実装することです。

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

これは次のように呼び出すことができます:

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

これは、Typeクラスの他のすべてのIsNullable()メソッドに適合するため、IsXxxx()にアクセスする論理的な方法にも思えます。

8
sclarke81

Null入力可能な型(Nullable<int>または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))

これは真の参照型になるため、null可能だったという事実を失います。

6

トピックから少し外れているかもしれませんが、それでも興味深い情報がいくつかあります。型がnull可能かどうかを識別するためにNullable.GetUnderlyingType() != nullを使用する人がたくさんいます。これは明らかに機能しますが、Microsoftは次のtype.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)http://msdn.Microsoft.com/en-us/library/ms366789.aspx を参照)にアドバイスします。

これをパフォーマンスの観点から見ました。以下のテスト(100万回の試行)の結論は、型がnull値を許容する場合、Microsoftオプションが最高のパフォーマンスを提供するということです。

Nullable.GetUnderlyingType():1335ms(3倍遅い)

GetGenericTypeDefinition()== typeof(Nullable <>):500ms

私たちは少しの時間について話していることを知っていますが、誰もがミリ秒を微調整するのが大好きです:-)!あなたが上司なら、数ミリ秒を減らしたいなら、これがあなたの救い主です...

/// <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();
}
3
Roel van Megen

少なくともPLC-Portable Class Library/。NET Core> = C#6で

解決策:Type TおよびNullable<T>の静的メソッドを拡張し、基礎となる静的拡張メソッドが一致するという事実を使用します。 typeが呼び出され、汎用の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;
    }
}

Reflectionとtype.IsGenericType...の使用は、現在の.NETランタイムのセットでは機能しませんでした。 MSDN Documentation も助けませんでした。

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

一部は、.NET CoreでReflection APIが大幅に変更されたためです。

0
Lorenz Lo Sauer

IsGenericTypeに対するMicrosoftの推奨テストを使用するものは良いと思いますが、GetUnderlyingTypeのコードでは、Microsoftは追加のテストを使用して、ジェネリック型定義Nullable<>を渡さないことを確認します。

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

このバージョン:

  • 結果のキャッシュが高速になり、
  • 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;
0
ipavlu