어떻게 든이 진술을 단축 할 수 있습니까?
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’입니다.