development

객체가 nullable인지 확인하는 방법?

big-blog 2020. 5. 13. 20:31
반응형

객체가 nullable인지 확인하는 방법?


주어진 객체가 nullable인지 확인하는 방법은 다음과 같습니다.

bool IsNullableValueType(object o)
{
    ...
}

편집 : nullable 값 형식을 찾고 있습니다. 심판 유형을 염두에 두지 않았습니다.

//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가 nullable인지 확인하고 obj 주위에 nullable 래퍼를 만들기로 결정했습니다.


널 입력 가능 유형 Nullable<T>과 참조 유형의 두 가지 유형이 있습니다.

Jon은 상자에 넣으면 입력하기가 어렵다는 것을 수정했지만 generics를 사용하면 다음과 같이 할 수 있습니다. 이것은 실제로 type 테스트 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
}

그러나 객체 변수에 이미 값을 상자에 넣으면 제대로 작동하지 않습니다.

Microsoft 설명서 : https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-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<>?", 일반화 할 수있는 "유형이 일부 제네릭 형식의 구성 형식 인 경우 확인하는 방법?"는 질문에 대답뿐만 아니라 그래서 "인가 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

객체가 null이 아닌 nullable인지 확인 Type하려면 위의 기술을 Marc 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);

...하지만 객체 자체는 nullable이 아니거나 유형아닙니다 . 이것을 어떻게 사용할 계획 이었습니까?


내가 알아낼 수있는 가장 간단한 방법은 다음과 같습니다.

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

여기에는 두 가지 문제가 있습니다. 1) 유형이 널 입력 가능 여부를 테스트; 2) 객체가 nullable Type을 나타내는 지 테스트

문제 1 (유형 테스트)의 경우 내 시스템에서 사용한 솔루션은 다음과 같습니다. TypeIsNullable-check solution

문제 2 (객체 테스트)의 경우 위의 Dean Chalk 솔루션은 값 유형에 대해 작동하지만 <T> 과부하를 사용하면 항상 false를 반환하므로 참조 유형에 대해서는 작동하지 않습니다. 참조 유형은 본질적으로 널 입력 가능하므로 참조 유형 테스트는 항상 true를 리턴해야합니다. 이러한 의미에 대한 설명은 아래의 "널링 가능성"정보를 참조하십시오. 따라서 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과 같은 메소드는 참조 유형 값을 처리 할 수 ​​있어야하고 모든 참조 유형이 본질적으로 널 입력 가능하므로 참조 유형 또는 널이 전달되면 메소드는 항상 true를 리턴해야합니다.

위의 두 가지 방법을 다음과 같은 단일 방법으로 대체하여 동일한 결과를 얻을 수 있습니다.

    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 가지 방법을 사용하는 것입니다.

주의 사항 :이 방법은 예제에 표시된 것처럼 원본 객체 참조 또는 정확한 사본을 사용하여 호출 된 경우에만 안정적으로 작동합니다. 그러나 nullable 객체가 원래 Nullable <> 형식으로 남아 있지 않고 다른 유형 (예 : 객체 등)으로 박스 화 된 경우이 메서드는 안정적으로 작동하지 않습니다. 이 메소드를 호출하는 코드가 원래의 언 박스 (unboxed) 객체 참조 또는 정확한 사본을 사용하지 않는 경우이 메소드를 사용하여 객체의 null 가능성을 확실하게 결정할 수 없습니다.

대부분의 코딩 시나리오에서, Null 허용 여부를 결정하려면 대신 참조가 아닌 원래 객체의 Type 테스트에 의존해야합니다 (예 : 코드는 Null 허용 여부를 결정하기 위해 객체의 원래 유형에 액세스해야합니다). 이러한보다 일반적인 경우 IsTypeNullable (링크 참조)은 Null 허용 여부를 결정하는 신뢰할 수있는 방법입니다.

PS- "널링 가능성"정보

별도의 게시물에서 작성한 Null 허용 여부에 대한 설명을 반복해야 하며이 주제를 올바르게 처리하는 데 직접 적용됩니다. 즉, 여기서 논의의 초점은 객체가 일반 Nullable 유형인지 확인하는 것이 아니라 유형의 객체에 null 값을 할당 할 수 있는지 여부를 확인하는 방법이어야한다고 생각합니다. 다시 말해, 객체 유형이 널 입력 가능 여부가 아닌 널 입력 가능 여부를 결정해야한다고 생각합니다. 차이점은 의미론, 즉 무효 성을 결정하기위한 실질적인 이유에 있습니다.

런타임까지 알려지지 않은 유형의 객체 (웹 서비스, 원격 호출, 데이터베이스, 피드 등)를 사용하는 시스템에서 일반적인 요구 사항은 객체에 null을 할당 할 수 있는지 또는 객체에 포함될 수 있는지 여부를 결정하는 것입니다. null 널 입력 가능하지 않은 유형에서 이러한 조작을 수행하면 성능 및 코딩 요구 사항 모두에서 매우 비싼 오류 (일반적으로 예외)가 발생할 수 있습니다. 이러한 문제를 사전에 피하는 것이 가장 선호되는 접근 방식을 취하려면 임의 유형의 오브젝트가 널을 포함 할 수 있는지 판별해야합니다. 즉, 일반적으로 '널링 가능'여부입니다.

매우 실용적이고 일반적인 의미에서 .NET 용어의 Null 허용 여부는 반드시 개체 유형이 Nullable 형식임을 의미하지는 않습니다. 실제로 많은 경우에 객체는 참조 유형을 가지며 널값을 포함 할 수 있으므로 모두 널 입력 가능합니다. 이들 중 어느 것도 널 입력 가능 유형이 없습니다. 따라서 대부분의 시나리오에서 실제적인 목적을 위해서는 Nullable의 구현 종속 개념과 비교하여 nullable의 일반적인 개념에 대해 테스트를 수행해야합니다. 따라서 우리는 .NET Nullable 유형에만 중점을 두지 말고 일반적인 Null 허용 개념에 초점을 맞추는 과정에서 요구 사항과 동작에 대한 이해를 통합하십시오.


내가 생각해 낸 가장 간단한 해결책 은 확장 방법으로 Microsoft 솔루션 ( 방법 : Nullable 형식 식별 (C # Programming Guide) )을 구현하는 것입니다.

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

그러면 다음과 같이 호출 될 수 있습니다.

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

이것은 클래스 IsNullable()의 다른 모든 IsXxxx()메소드에 적합하기 때문에 액세스하는 논리적 방법으로 보입니다 Type.


널 입력 가능 유형 ( 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))

실제 참조 유형이되므로 널 입력 가능하다는 사실을 잃게됩니다.


어쩌면 약간의 주제이지만 여전히 흥미로운 정보가 있습니다. Nullable.GetUnderlyingType() != null유형이 nullable 인 경우 ID 를 사용 하는 사람들이 많이 있습니다. 이것은 분명히 작동하지만 Microsoft는 다음을 조언합니다 type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)( http://msdn.microsoft.com/en-us/library/ms366789.aspx 참조 ).

나는 이것을 성능 측면에서 보았습니다. 아래 테스트 (100 만 회 시도)의 결론은 유형이 nullable 인 경우 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();
}

이 버전 :

  • 캐싱 결과가 더 빠릅니다.
  • 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;

Here is what I came up with, as everything else seemed to fail - at least on the PLC - Portable Class Library / .NET Core with >= C# 6

Solution: Extend static methods for any Type T and Nullable<T> and use the fact that the static extension method, matching the underlying type is going to be invoked and takes precedence over the generic T extension-method.

For T:

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

and for Nullable<T>

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

Using Reflection and type.IsGenericType... did not work on my current set of .NET Runtimes. Nor did the MSDN Documentation help.

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

In part because the Reflection API has been changed quite significantly in .NET Core.


I think the ones using Microsoft's suggested testing against IsGenericType are good, but in the code for GetUnderlyingType, Microsoft uses an additional test to make sure you didn't pass in the generic type definition Nullable<>:

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

a simple way to do this:

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

        return true;
    }

these are my unit tests and all passed

    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

actual unit tests

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

참고URL : https://stackoverflow.com/questions/374651/how-to-check-if-an-object-is-nullable

반응형