program story

속성의 속성이 null인지 확인하는 C # 우아한 방법

inputbox 2020. 9. 18. 08:08
반응형

속성의 속성이 null인지 확인하는 C # 우아한 방법


C #에서는이 예제에서 PropertyC의 값을 가져 오려고하며 ObjectA, PropertyA 및 PropertyB는 모두 null이 될 수 있습니다.

ObjectA.PropertyA.PropertyB.PropertyC

최소한의 코드로 PropertyC를 안전하게 얻으려면 어떻게해야합니까?

지금 확인합니다.

if(ObjectA != null && ObjectA.PropertyA !=null && ObjectA.PropertyA.PropertyB != null)
{
    // safely pull off the value
    int value = objectA.PropertyA.PropertyB.PropertyC;
}

이와 같은 (의사 코드) 더 많은 것을하는 것이 좋을 것입니다.

int value = ObjectA.PropertyA.PropertyB ? ObjectA.PropertyA.PropertyB : defaultVal;

null-coalescing 연산자로 훨씬 더 축소되었을 수 있습니다.

편집 원래 두 번째 예제는 js와 같았지만 js에서 작동하지 않을 것이라고 올바르게 지적되었으므로 가짜 코드로 변경했습니다.


C # 6에서는 Null 조건부 연산자를 사용할 수 있습니다 . 따라서 원래 테스트는 다음과 같습니다.

int? value = objectA?.PropertyA?.PropertyB?.PropertyC;

짧은 확장 방법 :

public static TResult IfNotNull<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
  where TResult : class where TInput : class
{
  if (o == null) return null;
  return evaluator(o);
}

사용

PropertyC value = ObjectA.IfNotNull(x => x.PropertyA).IfNotNull(x => x.PropertyB).IfNotNull(x => x.PropertyC);

이 간단한 확장 방법과 더 많은 것을 http://devtalk.net/csharp/chained-null-checks-and-the-maybe-monad/에서 찾을 수 있습니다.

편집하다:

잠시 사용한 후에이 메서드의 적절한 이름 은 원래 With () 대신 IfNotNull () 이어야한다고 생각합니다 .


수업에 메소드를 추가 할 수 있습니까? 그렇지 않다면 확장 방법 사용에 대해 생각해 보셨습니까? 라는 개체 유형에 대한 확장 메서드를 만들 수 있습니다 GetPropC().

예:

public static class MyExtensions
{
    public static int GetPropC(this MyObjectType obj, int defaltValue)
    {
        if (obj != null && obj.PropertyA != null & obj.PropertyA.PropertyB != null)
            return obj.PropertyA.PropertyB.PropertyC;
        return defaltValue;
    }
}

용법:

int val = ObjectA.GetPropC(0); // will return PropC value, or 0 (defaltValue)

그건 그렇고, 이것은 당신이 .NET 3 이상을 사용하고 있다고 가정합니다.


당신이하는 방식은 정확합니다.

당신은 할 수 설명 된 것과 같은 트릭을 사용 여기 의 LINQ 표현을 사용하여 :

int value = ObjectA.NullSafeEval(x => x.PropertyA.PropertyB.PropertyC, 0);

그러나 각 속성을 수동으로 확인하는 것이 훨씬 느립니다.


데메테르법칙 을 준수하기위한 리팩터링


2014 업데이트 : C # 6에는 ?.'안전한 탐색'또는 '널 전파'라는 다양한 새 연산자가 있습니다.

parent?.child

읽기 http://blogs.msdn.com/b/jerrynixon/archive/2014/02/26/at-last-c-is-getting-sometimes-called-the-safe-navigation-operator.aspx를 자세한 내용은

이것은 오랫동안 매우 인기있는 요청이었습니다 https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3990187-add-operator-to-c-?tracking_code=594c10a522f8e9bc987ee4a5e2c0b38d


분명히 Nullable Monad를 찾고 있습니다 .

string result = new A().PropertyB.PropertyC.Value;

된다

string result = from a in new A()
                from b in a.PropertyB
                from c in b.PropertyC
                select c.Value;

nullnullable 속성이 null이면을 반환합니다 . 그렇지 않으면 Value.

class A { public B PropertyB { get; set; } }
class B { public C PropertyC { get; set; } }
class C { public string Value { get; set; } }

LINQ 확장 방법 :

public static class NullableExtensions
{
    public static TResult SelectMany<TOuter, TInner, TResult>(
        this TOuter source,
        Func<TOuter, TInner> innerSelector,
        Func<TOuter, TInner, TResult> resultSelector)
        where TOuter : class
        where TInner : class
        where TResult : class
    {
        if (source == null) return null;
        TInner inner = innerSelector(source);
        if (inner == null) return null;
        return resultSelector(source, inner);
    }
}

이 코드는 "최소한의 코드"이지만 모범 사례는 아닙니다.

try
{
    return ObjectA.PropertyA.PropertyB.PropertyC;
}
catch(NullReferenceException)
{
     return null;
}

유형의 값이 비어 있다고 가정하면 한 가지 접근 방식은 다음과 같습니다.

var x = (((objectA ?? A.Empty).PropertyOfB ?? B.Empty).PropertyOfC ?? C.Empty).PropertyOfString;

저는 C #의 열렬한 팬이지만 새로운 Java (1.7?)에서 아주 좋은 점은.? 운영자:

 var x = objectA.?PropertyOfB.?PropertyOfC.?PropertyOfString;

이 블로그 게시물을 확인하십시오 . 체인 널 체크를위한 매우 우아한 방법이라고 생각합니다. 유사한 구현이 많이 있지만 체인에서 null이 발견되는 즉시 평가를 중지하기 때문에이 구현이 좋습니다.

모든 소스 코드는 github에 있습니다.


이와 같은 호출을 연결해야 할 때는 내가 만든 도우미 메서드 인 TryGet ()을 사용합니다.

    public static U TryGet<T, U>(this T obj, Func<T, U> func)
    {
        return obj.TryGet(func, default(U));
    }

    public static U TryGet<T, U>(this T obj, Func<T, U> func, U whenNull)
    {
        return obj == null ? whenNull : func(obj);
    }

귀하의 경우에는 다음과 같이 사용합니다.

    int value = ObjectA
        .TryGet(p => p.PropertyA)
        .TryGet(p => p.PropertyB)
        .TryGet(p => p.PropertyC, defaultVal);

새로운 C # 6.0에서 뭔가를 보았습니다. 이것은 '?'를 사용하는 것입니다. 확인하는 대신

예를 들어 사용하는 대신

if (Person != null && Person.Contact!=null && Person.Contact.Address!= null && Person.Contact.Address.City != null)
{ 
  var city = person.contact.address.city;
}

당신은 단순히 사용

var city = person?.contact?.address?.city;

누군가에게 도움이 되었기를 바랍니다.


최신 정보:

지금 이렇게 할 수 있습니다

 var city = (Person != null)? 
           ((Person.Contact!=null)? 
              ((Person.Contact.Address!= null)?
                      ((Person.Contact.Address.City!=null)? 
                                 Person.Contact.Address.City : null )
                       :null)
               :null)
            : null;

다음과 같이 할 수 있습니다.

class ObjectAType
{
    public int PropertyC
    {
        get
        {
            if (PropertyA == null)
                return 0;
            if (PropertyA.PropertyB == null)
                return 0;
            return PropertyA.PropertyB.PropertyC;
        }
    }
}



if (ObjectA != null)
{
    int value = ObjectA.PropertyC;
    ...
}

또는 더 좋을 수도 있습니다.

private static int GetPropertyC(ObjectAType objectA)
{
    if (objectA == null)
        return 0;
    if (objectA.PropertyA == null)
        return 0;
    if (objectA.PropertyA.PropertyB == null)
        return 0;
    return objectA.PropertyA.PropertyB.PropertyC;
}


int value = GetPropertyC(ObjectA);

It is not possible. ObjectA.PropertyA.PropertyB will fail if ObjectA is null due to null dereferencing, which is an error.

if(ObjectA != null && ObjectA.PropertyA ... works due to short circuiting, ie ObjectA.PropertyA will never be checked if ObjectA is null.

The first way you propose is the best and most clear with intent. If anything you could try to redesign without having to rely on so many nulls.


Just stumbled accross this post.

Some time ago I made a suggestion on Visual Studio Connect about adding a new ??? operator.

http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/4104392-add-as-an-recursive-null-reference-check-opera

이를 위해서는 프레임 워크 팀의 작업이 필요하지만 언어를 변경할 필요는 없지만 컴파일러 마법 만 수행하면됩니다. 아이디어는 컴파일러가이 코드를 변경해야한다는 것입니다 (구문은 atm이 허용되지 않음).

string product_name = Order.OrderDetails[0].Product.Name ??? "no product defined";

이 코드에

Func<string> _get_default = () => "no product defined"; 
string product_name = Order == null 
    ? _get_default.Invoke() 
    : Order.OrderDetails[0] == null 
        ? _get_default.Invoke() 
        : Order.OrderDetails[0].Product == null 
            ? _get_default.Invoke() 
            : Order.OrderDetails[0].Product.Name ?? _get_default.Invoke()

null 검사의 경우 다음과 같이 보일 수 있습니다.

bool isNull = (Order.OrderDetails[0].Product ??? null) == null;

다음 확장을 사용할 수 있으며 정말 좋다고 생각합니다.

/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<TF, TR>(TF t, Func<TF, TR> f)
    where TF : class
{
    return t != null ? f(t) : default(TR);
}

/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<T1, T2, TR>(T1 p1, Func<T1, T2> p2, Func<T2, TR> p3)
    where T1 : class
    where T2 : class
{
    return Get(Get(p1, p2), p3);
}

/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<T1, T2, T3, TR>(T1 p1, Func<T1, T2> p2, Func<T2, T3> p3, Func<T3, TR> p4)
    where T1 : class
    where T2 : class
    where T3 : class
{
    return Get(Get(Get(p1, p2), p3), p4);
}

그리고 다음과 같이 사용됩니다.

int value = Nulify.Get(objectA, x=>x.PropertyA, x=>x.PropertyB, x=>x.PropertyC);

Roslyn에서 제공하는 C # vNext에서 계획된 Null 전파


대답은 아니지만 더 많은 업데이트입니다. UserVoice가 Roslyn 이후로 다른 새로운 것들과 함께 이것을 주도한 것처럼 보이며 분명히 계획된 추가입니다.

https://roslyn.codeplex.com/discussions/540883


Nullable 형식과 유사한 패턴을 사용하여 PropertyA 형식 (또는 형식이 아닌 경우 확장 메서드)에 고유 한 메서드를 작성합니다.

class PropertyAType
{
   public PropertyBType PropertyB {get; set; }

   public PropertyBType GetPropertyBOrDefault()
   {
       return PropertyB != null ? PropertyB : defaultValue;
   }
}

기본값을 허용하는 메서드를 작성했습니다. 사용 방법은 다음과 같습니다.

var teacher = new Teacher();
return teacher.GetProperty(t => t.Name);
return teacher.GetProperty(t => t.Name, "Default name");

Here is the code:

public static class Helper
{
    /// <summary>
    /// Gets a property if the object is not null.
    /// var teacher = new Teacher();
    /// return teacher.GetProperty(t => t.Name);
    /// return teacher.GetProperty(t => t.Name, "Default name");
    /// </summary>
    public static TSecond GetProperty<TFirst, TSecond>(this TFirst item1,
        Func<TFirst, TSecond> getItem2, TSecond defaultValue = default(TSecond))
    {
        if (item1 == null)
        {
            return defaultValue;
        }

        return getItem2(item1);
    }
}

This approach is fairly straight-forward once you get over the lambda gobbly-gook:

public static TProperty GetPropertyOrDefault<TObject, TProperty>(this TObject model, Func<TObject, TProperty> valueFunc)  
                                                        where TObject : class
    {
        try
        {
            return valueFunc.Invoke(model);
        }
        catch (NullReferenceException nex)
        {
            return default(TProperty);
        }
    }

With usage that might look like:

ObjectA objectA = null;

Assert.AreEqual(0,objectA.GetPropertyOrDefault(prop=>prop.ObjectB.ObjectB.ObjectC.ID));

Assert.IsNull(objectA.GetPropertyOrDefault(prop => prop.ObjectB));

var result = nullableproperty ?? defaultvalue;

The ?? operator means if the first argument is null, return the second one instead.

참고URL : https://stackoverflow.com/questions/3468250/c-sharp-elegant-way-to-check-if-a-propertys-property-is-null

반응형