📜 ⬆️ ⬇️

Object initializers in using block

Object Initializers (Object Initializers) is a useful feature of the C # language, which allows you to initialize the necessary properties of an object right at the time of its creation. Since, syntactically, this “feature” is very close to initializing an object with passing parameters through a constructor, many developers begin to hammer on OOP principles (in particular, on the concept of an invariant) and use it wherever possible.

But even if we don’t go over to holivars and obscure terms, let's consider a small example and consider whether it can lead to problems or not:

// position     -  long position = -1; using (var file = new FileStream("d:\\1.txt", FileMode.Append) { //   ,     //   ! Position = position }) { //  -   } 

')
In this fragment, a resource (file) is created inside the using directive and one of its properties ( Position ) is set using the object initializer. In this case, the most important thing in this code is that the setter of this property can generate an exception.

Unlike C ++ in .NET, we rarely encounter exceptions security problems, but this is one of those rare cases where code is unsafe from the point of view of exceptions .

To understand this, let's see how the object's initializer is implemented by the compiler:

 class Person { public string Name { get; set; } public int Age { get; set; } } // ... var person = new Person {Name = "John", Age = 42}; 


At first glance, it may seem that the object's initializer is nothing more than a call to the constructor and then change its properties. And, in fact, the way it is, with only a small clarification:

 var tmp = new Person(); tmp.Name = "Jonh"; tmp.Age = 42; var person = tmp; 


A temporary variable is a necessary condition for the "atomicity" of initialization, without which third-party code (for example, from another thread) could get a reference to an object in an intermediate state. In addition, the absence of a temporary variable would make the code even less safe from the point of view of exceptions, because then generating an exception from the setter property would result in partial initialization of the variable:

 var tmp = new Person(); tmp.Name = "John"; tmp.Age = 42; var person = tmp; 


In this case, if the setter of one of the properties falls with the exception, then the _ person field will already be initialized, but not completely, which would violate the "atomicity" of the object initializer, which is so familiar with the use of constructors.

However, although the temporary variable solves a number of problems, this does not happen when using an object initializer within the using directive. As you know, the using directive expands to this code:

 var file = new FileStream("d:\\1.txt", FileMode.OpenOrCreate); try {} finally { if (file != null) ((IDisposable)file).Dispose(); } 


Now, if we add up 2 and 2, then we get that our original example turns into the following:

 long position = -1; var tmpFile = new FileStream("d:\\1.txt", FileMode.OpenOrCreate); // !    ,  Dispose   ! tmpFile.Position = position; var file = tmpFile; try { } finally { if (file != null) ((IDisposable)file).Dispose(); } 


And this means that if a property initialized with an object's initializer falls with an exception, the Dispose method of our object will not be called.

Conclusion

Object initializers are a useful feature, but you need to be able to use it as well. Although syntactically (and semantically, by the way, too) this possibility is very close to calling the constructor, but, unfortunately, they are not always equivalent. As for the principles of OOP, then it is up to you to decide when to use an object initializer, and when not, but when it comes to exceptions security and resource management, then you definitely need to understand how this feature works and what follows from this.

C # language has a rather predictable behavior in most cases (although there are exceptions, such as subtleties with variable significant types ), but mixing object initializers with a using block doesn’t differ by intuitiveness and it is desirable to understand exactly how this combination works so as not to shoot yourself carelessness.

Source: https://habr.com/ru/post/144240/


All Articles