次の方法で共有


onevent Code Snippet

When raising events, the .NET framework design guidelines recommend that this is done from a protected virtual method named On<EventName> (unless the class is sealed or the event is static, etc.). This leads to a recurring pattern of similarly looking code like this:

 protected virtual void OnMyEvent(MyEventArgs e)
{
    if (this.MyEvent != null)
    {
        this.MyEvent(this, e);
    }
}

After having done this numerous times it struck me as a good candidate for a code snippet, so I decided to create the onevent code snippet. Creating a code snippet is pretty easy; from my decision to create the snippet to using it for the first time, I spent less than half an hour. Here's how:

Create a new XML file. For the onevent code snippet, the contents look like this:

 <?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="https://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>onevent</Title>
            <Shortcut>onevent</Shortcut>
            <Description>Code snippet for creating a method for raising an event</Description>
            <Author>Mark Seemann</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>event</ID>
                    <ToolTip>The event to raise</ToolTip>
                    <Default>Event</Default>
                </Literal>
                <Literal>
                    <ID>eventargs</ID>
                    <ToolTip>The type of event args for the event</ToolTip>
                    <Default>EventArgs</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp"><![CDATA[protected virtual void On$event$($eventargs$ e)
    {
        if (this.$event$ != null)
        {
            this.$event$(this, e);
        }
    }]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

Each snippet consists of a header and the snippet itself. The header mostly contains metadata like the title, the shortcut used in the code editor, etc. The only vaguely puzzling element is the SnippetTypes element, where you must specify the type of code snippet. Expansion just indicates that the code snippet will be inserted at the current location of the cursor (see the Code Snippet Schema Reference at https://msdn2.microsoft.com/en-us/library/ms171418.aspx for a full description of the possible values).

The Snippet section of the XML file consists of a Declarations section and the actual code for the snippet. Declarations is used to specify the placeholders for your code expressions - in this case, the name of the event, and the type of the event args.

The actual code is defined in a CDATA section, where each declaration is referenced by its name pre- and postfixed with $.

To start using the code snippet, just save the XML file with the .snippet extension to the C:\Program Files\Microsoft Visual Studio 8\VC#\Snippets\1033\Visual C# folder and type onevent followed by the Tab key in your code editor. Visual Studio picks up the new code snippet immediately, so you don't even have to restart the IDE!

Obviously, you need to have already defined the EventArgs class and the event itself, but with this in place you now have an instant event raiser method, which follows the framework design guidelines.

Update: Several people have been so kind to point out to me that my code is not thread-safe (see the comments to this post). For that, I humbly apologize. In a new comment, I've posted the updated XML for the code snippet, including the thread-safe code.

Additionally, I have also included the updated snippet as an attachment to the post, so now you can just download the file to your C:\Program Files\Microsoft Visual Studio 8\VC#\Snippets\1033\Visual C# folder to begin using it.

onevent.snippet

Comments

  • Anonymous
    March 17, 2006
    this mode of event invokation is not thread safe. it's possible to null the event handler between the 'if' and the real invokation.

    use this:

    EventHandlerType e = this.EventHandler;
    if (e != null)
     e(...);
  • Anonymous
    March 17, 2006
    The comment has been removed
  • Anonymous
    March 17, 2006
    Excuse me, but such code snippet ALREADY EXISTS!

    And it is thread safe. By the way, I was participating in enhancing it at beta phase.

    The snippet has name "invoke".

    Also, note that second way is definitely thread-safe. Pointer cannot became null here.
  • Anonymous
    March 17, 2006
    Oleg, thanks for pointing out the invoke method, which I wasn't aware of.

    This snippet provides part of the functionality of the onevent snippet, but doesn't wrap the event invokation in a protected virtual method. It was actually that part (the method signature, and conformance to the framework design guidelines) that interested me, more than the actual implementation of the event raising code. The invoke snippet doesn't create the method itself, whereas the onevent snippet does.

    As I wrote: I'm no expert in threading, and it would seem I'm wrong about the thread-safety issue. However, I always want to learn, so could you perhaps explain to me why the pointer cannot become null between the conditional and the invokation?
  • Anonymous
    March 17, 2006
    Memory allocated on the stack, as is the case with 'e', can not be modified by another thread (or process). The 'this' pointer lives on the heap, that is the shared memory space, and can as such be modified by other threads. If you had put a lock around the if-block it would work as well.
  • Anonymous
    March 17, 2006
    Hi Karsten

    Thank you for joining the discussion :)

    We are getting a bit closer to my original disclaimer about delegates behaving like value types... What I still don't understand (completely) is why the EventHandler would be allocated on the stack?

    This is probably just due to lack of knowledge on my part: I know that value types live on the stack; a value type is just a special kind of type that is characterized by being allocated on the stack instead of on the heap. A delegate is also a rather special kind of type - are they also, by definition, allocated on the stack?
  • Anonymous
    March 17, 2006
    Ploeh,

    the allocation on stack is not about EventHandler, but reference itself. The reference is on stack, but it point to heap-allocated object EventHandler.

    Such story is always in place when we see local variable to reference type. Variable itself is on stack (obviously) but it points to some object on heap.

    So, the reference itself cannot be modified in this example. Because reference lives in pure local storage — stack. No other code can modify the variable value. Other code may modify fields of same objects, and that lead to non-actual value of local variable. But nobodey can change local variable, only method itself.

    This method of handling thread-safety is very well and recommended. You grab some state BEFORE ACTUAL OPERATING in private storage like stack, and then operate on it without any worrying about threading issues. By the way, such method does not apply any performance penalty and is free from deadlock problems.


    P.S. I agree that invoke is not equal to your solution. There are many places where such additional code snippets would be very useful. It seems VS developers had no time to complete code snippet library to date of release.
    ---
    Oleg Mihailik, Developer Security MVP
    Kyiv, Ukraine
  • Anonymous
    March 17, 2006
    Oleg, thank you for the clarification - I didn't know that local variables are allocated on the stack (even if they point to objects on the heap), but now I do :)

    I'm happy I just learned something new today, and this method of creating a local variable at the beginning of some operation is a good trick to know.

    I agree that the onevent code snippet should include this extra step, and I'll definitely be updating my local copy (however, I'm not going to change the blog post itself, since that would make this whole discussion rather meaningless).
  • Anonymous
    March 19, 2006
    For those who are interested, here's the updated, thread-safe code snippet:

    <?xml version="1.0" encoding="utf-8" ?><CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">    <CodeSnippet Format="1.0.0">        <Header>            <Title>onevent</Title>            <Shortcut>onevent</Shortcut>            <Description>Code snippet for creating a method for raising an event</Description>            <Author>Mark Seemann</Author>            <SnippetTypes>                <SnippetType>Expansion</SnippetType>            </SnippetTypes>        </Header>        <Snippet>            <Declarations>                <Literal>                    <ID>event</ID>                    <ToolTip>The event to raise</ToolTip>                    <Default>Event</Default>                </Literal>                <Literal>                    <ID>eventargs</ID>                    <ToolTip>The type of event args for the event</ToolTip>                    <Default>EventArgs</Default>                </Literal>            </Declarations>            <Code Language="csharp"><![CDATA[protected virtual void On$event$($eventargs$ e)    {        EventHandler<$eventargs$> handler = this.$event$;        if (handler != null)        {            handler(this, e);        }    }]]>            </Code>        </Snippet>    </CodeSnippet></CodeSnippets>
  • Anonymous
    March 19, 2006
    So, a couple of days ago I discovered just how easy it is to create code snippets for Visual Studio 2005,...