-->

Assigning events in object initializer

2019-01-12 05:53发布

问题:

Why isn't it possible to assign events along with properties in object initializers in C#? It seems to be so natural to do so.

var myObject = new MyClass()
     {
        Property = value,
        Event1 = actor,
        // or
        Event2 += actor
     };  

Or is there some trick that I don't know of?

回答1:

As far the external contract is concerned, an event doesn't have a setter, only add and remove methods - subscribers can register and unregister from the event, and the publishing object decides when to invoke the callbacks by 'raising' the event. Consequently, the idea of "assigning an event", in general, is meaningless.

However, when you declare an event in a class, the C# compiler provides you with what is really a convenience-feature: when you don't provide your own implementation, it creates a private, backing delegate-field for you, along with the appropriate add / remove implementations . This allows you to "set the event" (really the backing field) within the class, but not outside it. To understand this, consider:

public class Foo
{
    // implemented by compiler
    public event EventHandler MyEvent;

    public static Foo FooFactory(EventHandler myEventDefault)
    {
       // setting the "event" : perfectly legal
       return new Foo { MyEvent = myEventDefault }; 
    }
}

public class Bar
{
    public static Foo FooFactory(EventHandler myEventDefault)
    {
        // meaningless: won't compile
        return new Foo { MyEvent = myEventDefault };
    }
}


public class Baz
{
    // custom implementation
    public event EventHandler MyEvent
    {      
        add { }  // you can imagine some complex implementation here
        remove { } // and here
    }

    public static Baz BazFactory(EventHandler myEventDefault)
    {
        // also meaningless: won't compile
        return new Baz { MyEvent = myEventDefault };
    }
}


回答2:

You can only use += or -+ operators to an event outside its owner class.

public class Data
{
   public event EventHandler OnSave = (s,e) => 
      {
         //do something important!
      };

   public void Save()
   {
      OnSave(this,null);
      //do save
   }
}
//outside the class
Data data = new Data { OnSave = null }; //compile error
data.OnSave = SomeMethodElse;  //compile error
data.OnSave += MyCustomActionsOnSave;  //ok
data.Save();

You can't remove the OnSave action defined in the class. You can only add/remove your own OnSave actions outside the class. If you remove the event keyword, the OnSave will be no longer an event, but an ordinary delegate. Then you can do anything including assigning value outside the class.



回答3:

This didn't make C# 6 or C# 7 (since the original question), however it hasn't been decided against. There is an issue on GitHub that tracks the language proposal. You can vote for it there, as well as follow links into previous discussion around the feature.

https://github.com/dotnet/csharplang/issues/307

If you'd like to see this feature, add a thumbs-up to the issue to help raise its visibility.

The proposed syntax is:

var timer = new DispatcherTimer {
    Tick += delegate {},
    Interval = TimeSpan.FromSeconds(1d),
};