イベントハンドラがnullかどうかをチェックするとき、これはスレッドごとに行われますか?
誰かがそのイベントを確実に聞いているようにするには、次のようにします。
EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen);
上記のパターンに従ってnullをチェックする場所にコードを追加すると、なぜnullチェックが必要になるのでしょうか。このサイトから取得したコード)何が足りないの?
また、イベントとGCに関する規則は何ですか?
あなたが私が恐れているとはどういう意味かはっきりしませんが、デリゲートがnullになる可能性がある場合は、各スレッドで個別に確認する必要があります。通常は次のようにします。
public void OnSeven()
{
DivBySevenHandler handler = EventSeven;
if (handler != null)
{
handler(...);
}
}
これにより、EventSeven
の過程で変化するOnSeven()
あなたは得ることはありませんNullReferenceException
。
しかし、あなたが間違いなくサブスクライブされたハンドラを持っているならば、あなたはあなたがnullチェックを必要としないのは正しいです。これは、 "no-op"ハンドラを使ってC#2で簡単に実行できます。
public event DivBySevenHandler EventSeven = delegate {};
一方、あなたはたぶんさまざまなスレッドからサブスクリプションを取得する可能性がある場合に、確実に「最新の」一連のハンドラーを取得していることを確認するために、ある種のロックを使用します。を持っています私のスレッディングチュートリアルの例それは助けになることができます - 通常私はそれを必要としないようにすることを試みることをお勧めしますが。
ガベージコレクションの面では、イベント出版社イベントへの参照で終わる加入者(つまり、ハンドラのターゲット)これは、発行者が購読者よりも長生きすることを意図している場合にのみ問題になります。
EventSeven?.Invoke(....)
。それはスレッドセーフですか? - Hossein Narimani Rad
問題は、誰もそのイベントを購読していない場合、それがnullであることです。そして、あなたはnullに対して呼び出すことはできません。 3つのアプローチが頭に浮かぶ。
public event EventHandler MyEvent = delegate {};
nullをチェックするとき、スレッドセーフにするためには、理論的には最初にデリゲート参照をキャプチャします(チェックと呼び出しの間で変化する場合)。
protected virtual void OnMyEvent() {
EventHandler handler = MyEvent;
if(handler != null) handler(this, EventArgs.Empty);
}
拡張メソッドは、nullインスタンスで呼び出すことができるという珍しい性質を持っています...
public static void SafeInvoke(this EventHandler handler, object sender)
{
if (handler != null) handler(sender, EventArgs.Empty);
}
public static void SafeInvoke<T>(this EventHandler<T> handler,
object sender, T args) where T : EventArgs
{
if (handler != null) handler(sender, args);
}
それからあなたは呼び出すことができます:
MyEvent.SafeInvoke(this);
そしてそれはnullチェック(チェックによる)とスレッドセーフ(参照を一度だけ読むことで)の両方です。
C#6.0構文に関する短い情報を追加したいと思います。
これを置き換えることが可能になりました:
var handler = EventSeven;
if (handler != null)
handler.Invoke(this, EventArgs.Empty);
これとともに:
handler?.Invoke(this, EventArgs.Empty);
protected virtual void OnMyEvent()
{
EventHandler handler = MyEvent;
handler?.Invoke(this, EventArgs.Empty);
}
までワンライナー:
protected virtual void OnMyEvent() => MyEvent?.Invoke(this, EventArgs.Empty);
MyEvent?.Invoke
安全ですhandler?.Invoke
。 null伝播チェックはアトミックですか? - Teejay
イベントハンドラを起動する前にチェックすることは常に良い習慣です。たとえ私が最初にそれが常に設定されていることを私自身に「保証」したとしても、私はこれをする。後でこれを変更した場合、イベントの発生をすべてチェックする必要はありません。そのため、各イベントには常に次のようなOnXXXメソッドが付随しています。
private void OnEventSeven()
{
var handler = EventSeven;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
外部の呼び出し元が自由にイベントハンドラを追加したり削除したりできるので、イベントハンドラがクラスに対してパブリックである場合、これは特に重要です。
あなたがこれを意味するならば:
public static void OnEventSeven(DivBySevenEventArgs e)
{
if(EventSeven!=null)
EventSeven(new object(),e);
}
コードの一部、そして答えは:
誰も "EventSeven"イベントハンドラを購読していない場合は、 "EventSeven(new object()、e);"でnull参照例外が発生します。
そして規則:
サブスクライバは、イベントを受信したくなくなったときにハンドラを追加し(+ =)、削除します( - =)。オブジェクトが参照されなくなった場合、ガベージコレクションはデフォルトの規則に従って処理されます。
this
ランダムに新しく作成されたオブジェクトではなく) - Rowland Shaw
を使うポストシャープコンパイル後の手順でコンパイル済みアセンブリを調整することは可能です。これにより、コードに「アスペクト」を適用して、分野横断的な問題を解決することができます。
nullチェックや空のデリゲートの初期化は非常に小さな問題かもしれませんが、アセンブリ内のすべてのイベントに空のデリゲートを追加することで解決するという側面を書きました。
使い方はとても簡単です。
[assembly: InitializeEventHandlers( AttributeTargetTypes = "Main.*" )]
namespace Main
{
...
}
私私のブログで詳細にアスペクトについて議論しました。あなたがPostSharpを持っている場合には、これがアスペクトです:
/// <summary>
/// Aspect which when applied on an assembly or class, initializes all the event handlers (<see cref="MulticastDelegate" />) members
/// in the class(es) with empty delegates to prevent <see cref="NullReferenceException" />'s.
/// </summary>
/// <author>Steven Jeuris</author>
[AttributeUsage( AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Event )]
[MulticastAttributeUsage( MulticastTargets.Event, AllowMultiple = false )]
[AspectTypeDependency( AspectDependencyAction.Commute, typeof( InitializeEventHandlersAttribute ) )]
[Serializable]
public class InitializeEventHandlersAttribute : EventLevelAspect
{
[NonSerialized]
Action<object> _addEmptyEventHandler;
[OnMethodEntryAdvice, MethodPointcut( "SelectConstructors" )]
public void OnConstructorEntry( MethodExecutionArgs args )
{
_addEmptyEventHandler( args.Instance );
}
// ReSharper disable UnusedMember.Local
IEnumerable<ConstructorInfo> SelectConstructors( EventInfo target )
{
return target.DeclaringType.GetConstructors( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
}
// ReSharper restore UnusedMember.Local
public override void RuntimeInitialize( EventInfo eventInfo )
{
base.RuntimeInitialize( eventInfo );
// Construct a suitable empty event handler.
MethodInfo delegateInfo = DelegateHelper.MethodInfoFromDelegateType( eventInfo.EventHandlerType );
ParameterExpression[] parameters = delegateInfo.GetParameters().Select( p => Expression.Parameter( p.ParameterType ) ).ToArray();
Delegate emptyDelegate
= Expression.Lambda( eventInfo.EventHandlerType, Expression.Empty(), "EmptyDelegate", true, parameters ).Compile();
// Create a delegate which adds the empty handler to an instance.
_addEmptyEventHandler = instance => eventInfo.AddEventHandler( instance, emptyDelegate );
}
}
...そしてそれが使用するヘルパーメソッド:
/// <summary>
/// The name of the Invoke method of a Delegate.
/// </summary>
const string InvokeMethod = "Invoke";
/// <summary>
/// Get method info for a specified delegate type.
/// </summary>
/// <param name = "delegateType">The delegate type to get info for.</param>
/// <returns>The method info for the given delegate type.</returns>
public static MethodInfo MethodInfoFromDelegateType( Type delegateType )
{
Contract.Requires( delegateType.IsSubclassOf( typeof( MulticastDelegate ) ), "Given type should be a delegate." );
return delegateType.GetMethod( InvokeMethod );
}