Good day to all. There was a question about C # events.

We have an Engine (Engine).

Engine has ModelParams model parameters. For example, the parameters include the countOfClapans valve count. In ModelParams, the paramsChanged event is defined, signaling a change in model parameters.

During the design process, I had a question: how to protect the parameters of the model from changing through sender from the paramsChanged event handler? At the same time, I do not want to make the properties of the parameters of the model of the type {get; private set {}} to disable the ability to edit properties outside the class.

As I understand it, you can make separate events to change each parameter of the model. But what if there are many of these parameters? Do not do the same number of event handlers for events.

In short, I try to isolate a ModelParams object from other objects (in this case, Cylinder class objects) that will subscribe to a model change event.

Below is the code in a very simplified form:

public class Engine { public ModelParams modelParams; public List<Cylinder> cylinders = new List<Cylinder>(); public Engine( ModelParams modelParams ) { this.modelParams = modelParams; } } public class ModelParams { private int countOfClapans; private string engineType; public int ClapansCount { get { return countOfClapans; } set { countOfClapans = value; } }; public int TypeOfEngine { get { return engineType; } set { engineType= value; } }; public event EventArgs<ModelArgs> ModelChanged; public void OnModelChange() { if(modelChanged != null) { ModelArgs args = new ModelArgs(); args.ClapansCount = this.ClapansCount; ModelChanged(this, args); } } } public class ModelArgs{ private int countOfClapans; private string engineType; public int ClapansCount { get { return countOfClapans; } set { countOfClapans= value; } }; public int TypeOfEngine { get { return engineType; } set { engineType= value; } }; } 
  • one
    no way, if you have a link to the object itself - you can always change its properties - Grundy
  • make the fields readonly or remove the setter ... - if you don’t want to change it, why bother to create it ?? and access to the field in the class remains - Volodymyr
  • I planned to change the model parameters from the outside of the Engine class. - Eladei
  • one
    What do you mean protect? Protect from artmoney? What changes? From other classes? From other assembly? - nick_n_a February
  • Or it can do what in set worked once, i.e. if you put 4 then 2 could not change? - nick_n_a

2 answers 2

The correct method seems to me to give the readonly interface to the outside. Declare an interface with getters and without setters IModelParams , and use it in defining your event .


If you want to protect yourself from those who can maliciously do a reverse caste to ModelParams , there are several ways.

You can bring the ModelParams to a separate assembly, mark the setters as internal , and put everyone who should have access to the setters into the same assembly.

Or you can write a read-only-wrapper, which will contain the object as a private field, and give out only getters (and in the getters, take the value from the real object).


These decisions, in turn, will not work against reflection. If someone maliciously uses reflection in order to gain access to private fields, he needs to start tapping on the brain through an architect.

And if you really want to protect yourself from such technical, rather than administrative means, download their code into a separate AppDomain with reduced rights, so you can prevent them from using reflection. But I would still recommend the path of a kind word, documentation, and suggestion on the part of the architect: working with code that you don’t trust is a painful and ungrateful task.


Update: I re-read your code, and I understood exactly what I misunderstood:

Your event , apparently, is defined as

 public event EventHandler<ModelArgs> ModelChanged; // а не EventArgs<ModelArgs> 

and what do you actually pass to event ModelParams as sender , the type of which is still object ? If so, then this means that your customers do a downcast to ModelParams . This means that simply creating an interface does not help, and you need to use the second option: creating a wrapper class or making the ModelParams type into a separate assembly.

  • VladD, thank you so much for such a detailed response! I correctly understood that you should not overdo it with reinsurance. That is, in such cases, you can do the usual wrapper in the form of an interface, believing that the event handler will not do the reverse cast? It turns out that the Engine class object in the constructor must be done: Engine (IModelParams modelParams). If I understand correctly, the question is closed) - Eladei
  • @Eladei: Yes, I think that just exporting the interface should be enough. // You are welcome! - VladD
  • The last question is to make sure I understand you correctly). Did you talk about importing the interface? Indeed, in the previous message, an object that supports the interface was entered into the constructor. - Eladei
  • @Eladei: By export, I meant that your event exposes the interface type, not the object type. - VladD
  • Thank you very much for your answer. I stopped at the next option, I hope that it is optimal. A wrapper class is created around IModelDiesel. Access to this object is available only in read mode. Modification is available within the class using the Update method. Inside the Update method, an object of the class that implements IModelDiesel is supplied. Thus, I can calmly change the properties of an object of the ModelDiesel class outside the wrapper, and then submit it to the Update method. Then a Cylinder class object subscribes to a wrapper class event. This event will expose the type-object, and changing IModelDiesel will not work. - Eladei

In C #, a delegate with any signature can act as an event. EventHandler with its two parameters is a classic version, and it is used everywhere. But with great need you can declare an event like this:

 public delegate void ModelChangeEventHandler(ModelArgs e); public event ModelChangeEventHandler ModelChanged; // или так public event Action<ModelArgs>; 
  • Thanks for the advice). Indeed, one should not rigidly bind oneself to one single method, as to dogma). - Eladei
  • one
    A good option, by the way. - VladD February