[c#] C #에서 null이 아닌 경우 메서드 호출

어떻게 든이 진술을 단축 할 수 있습니까?

if (obj != null)
    obj.SomeMethod();

이 글을 많이 써서 꽤 짜증이 나기 때문입니다. 내가 생각할 수있는 유일한 방법은 Null Object 패턴 을 구현하는 것입니다 .하지만 그것은 매번 할 수있는 일이 아니며 구문을 단축하는 해결책도 아닙니다.

그리고 이벤트와 유사한 문제는

public event Func<string> MyEvent;

그런 다음 호출

if (MyEvent != null)
    MyEvent.Invoke();



답변

C # 6부터는 다음을 사용할 수 있습니다.

MyEvent?.Invoke();

또는:

obj?.SomeMethod();

?.널 전파 연산자이며,이 발생할 .Invoke()오퍼랜드 인 경우 단락 될 null. 피연산자는 한 번만 액세스되므로 “확인 및 호출 사이의 값 변경”문제의 위험이 없습니다.

===

C # 6 이전에는 아니요 : 한 가지 예외를 제외하고는 null-safe 마법이 없습니다. 확장 방법-예 :

public static void SafeInvoke(this Action action) {
    if(action != null) action();
}

이제 이것은 유효합니다.

Action act = null;
act.SafeInvoke(); // does nothing
act = delegate {Console.WriteLine("hi");}
act.SafeInvoke(); // writes "hi"

이벤트의 경우 경쟁 조건을 제거 할 수 있다는 장점이 있습니다. 즉, 임시 변수가 필요하지 않습니다. 따라서 일반적으로 다음이 필요합니다.

var handler = SomeEvent;
if(handler != null) handler(this, EventArgs.Empty);

하지만 함께:

public static void SafeInvoke(this EventHandler handler, object sender) {
    if(handler != null) handler(sender, EventArgs.Empty);
}

간단하게 사용할 수 있습니다.

SomeEvent.SafeInvoke(this); // no race condition, no null risk


답변

당신이 찾고있는 것은 Null Conditional ( “coalescing”아님) 연산자 : ?.. C # 6부터 사용할 수 있습니다.

귀하의 예는 obj?.SomeMethod();. obj가 null이면 아무 일도 일어나지 않습니다. 메소드에 인수가있는 경우, 예를 들어 obj?.SomeMethod(new Foo(), GetBar());인수 obj가 null이면 평가되지 않습니다 . 인수 평가에 부작용이 있는지 여부가 중요합니다.

그리고 연결이 가능합니다. myObject?.Items?[0]?.DoSomething()


답변

빠른 확장 방법 :

    public static void IfNotNull<T>(this T obj, Action<T> action, Action actionIfNull = null) where T : class {
        if(obj != null) {
            action(obj);
        } else if ( actionIfNull != null ) {
            actionIfNull();
        }
    }

예:

  string str = null;
  str.IfNotNull(s => Console.Write(s.Length));
  str.IfNotNull(s => Console.Write(s.Length), () => Console.Write("null"));

또는 대안으로 :

    public static TR IfNotNull<T, TR>(this T obj, Func<T, TR> func, Func<TR> ifNull = null) where T : class {
        return obj != null ? func(obj) : (ifNull != null ? ifNull() : default(TR));
    }

예:

    string str = null;
    Console.Write(str.IfNotNull(s => s.Length.ToString());
    Console.Write(str.IfNotNull(s => s.Length.ToString(), () =>  "null"));


답변

이벤트는 제거되지 않는 빈 기본 델리게이트로 초기화 할 수 있습니다.

public event EventHandler MyEvent = delegate { };

널 검사가 필요하지 않습니다.

[ 업데이트 ,이를 지적 해준 Bevan에게 감사드립니다]

그러나 가능한 성능 영향을 인식하십시오. 내가 한 빠른 마이크로 벤치 마크에 따르면 “기본 델리게이트”패턴을 사용할 때 구독자가없는 이벤트를 처리하는 것이 2-3 배 느립니다. (저의 듀얼 코어 2.5GHz 노트북에서는 279ms : 5 천만 개의 미가입 이벤트를 발생시키는 데 785ms를 의미합니다.) 애플리케이션 핫스팟의 경우 고려해야 할 문제가 될 수 있습니다.


답변

예, C # 6.0- https : //msdn.microsoft.com/en-us/magazine/dn802602.aspx .

object?.SomeMethod()


답변

Ian Griffiths 의이 기사는 그가 사용하지 말아야 할 깔끔한 트릭이라고 결론 내린 문제에 대한 두 가지 다른 해결책을 제공합니다.


답변

제안 된 것과 같은 확장 확장 방법은 경합 조건 문제를 실제로 해결하지 않고 숨기는 방법입니다.

public static void SafeInvoke(this EventHandler handler, object sender)
{
    if (handler != null) handler(sender, EventArgs.Empty);
}

언급했듯이이 코드는 임시 변수가있는 솔루션과 동일하지만 …

두 가지 문제 는 이벤트 구독자가 이벤트 구독을 취소 한 후 호출 될 수 있다는 것 입니다. 델리게이트 인스턴스가 임시 변수에 복사 된 후 (또는 위 메서드에서 매개 변수로 전달 된 후) 델리게이트가 호출되기 전에 구독 취소가 발생할 수 있기 때문에 가능합니다.

일반적으로 클라이언트 코드의 동작은 이러한 경우 예측할 수 없습니다. 구성 요소 상태에서 이미 이벤트 알림을 처리 할 수 ​​없습니다. 이를 처리하는 방식으로 클라이언트 코드를 작성하는 것은 가능하지만 클라이언트에게 불필요한 책임을 부과합니다.

스레드 안전성을 보장하는 유일한 방법은 이벤트 발신자에 대해 잠금 문을 사용하는 것입니다. 이렇게하면 모든 subscription \ unsubscriptions \ invocation이 직렬화됩니다.

보다 정확한 잠금을 위해서는 add \ remove 이벤트 접근 자 메서드에 사용 된 것과 동일한 동기화 개체에 적용되어야합니다. 기본값은 ‘this’입니다.