Hi I use initializer block in C#
new Something { foo = 1, bar = 2 };
but people say this is bad practice.
I don't think it is wrong, is it?
Instance Initializer block is used to initialize the instance data member. It run each time when object of the class is created. The initialization of the instance variable can be done directly but there can be performed extra operations while initializing the instance variable in the instance initializer block.
In order to perform any operations while assigning values to an instance data member, an initializer block is used. In simpler terms, the initializer block is used to declare/initialize the common part of various constructors of a class. It runs every time whenever the object is created.
Initializer block contains the code that is always executed whenever an instance is created. It is used to declare/initialise the common part of various constructors of a class. The order of initialization constructors and initializer block doesn't matter, initializer block is always executed before constructor.
The Java compiler copies initializer blocks into every constructor. Therefore, this approach can be used to share a block of code between multiple constructors. This is especially useful if subclasses might want to reuse the initialization method.
You need to ask yourself whether your type should be mutable or not. Personally, I like immutable types - they make it easier to reason about what's going on, easier to validate (once the constructor has been called and the state validated, you know it's not going to become invalid) and they're great for concurrency.
On the other hand, object initializers are certainly useful in cases where it is reasonable to have mutable types. As an example, ProcessStartInfo
is effectively used as a builder type for Process
. It's useful to be able to write:
var info = new ProcessStartInfo {
FileName = "notepad.exe",
Arguments = "foo.txt",
ErrorDialog = true
};
Process process = Process.Start(info);
Indeed, you can even do all this inline instead of having an extra variable. My Protocol Buffers port uses the same sort of pattern:
Foo foo = new Foo.Builder {
FirstProperty = "first",
SecondProperty = "second"
}.Build();
Now one alternative to the builder pattern is constructor parameters (possibly via factory methods). The historical downside of this is that you needed different overloads depending on which properties were being set, and if several parameters had the same type it could be hard to tell which was which. C# 4 makes this significantly easier using optional parameters and named arguments. For example, if you're building an email class you could have:
Email email = new Email(
from: "[email protected]",
to: "[email protected]",
subject: "Test email",
body: textVariable
);
This has many of the same benefits of object initializers in terms of clarity, but without the mutability penalty. The constructor call above may have missed out some optional parameters such as attachments and a BCC list. I think this will prove to be one of the biggest benefits of C# 4 for those of us who like immutability but also like the clarity of object initializers.
It's questionable (I won't say "bad") practice to use initialization blocks as a substitute for the appropriate constructor overload, if one exists.
public class Entity
{
public Entity()
{
}
public Entity(int id, string name)
{
this.ID = id;
this.Name = name;
}
public int ID { get; set; }
public string Name { get; set; }
}
If you have this very simple class, then it is generally preferable to write:
var entity = new Entity(1, "Fred");
...than it is to write:
var entity = new Entity { ID = 1, Name = "Fred" };
There are at least two good reasons for this:
You don't know exactly what the constructor is doing. It's possible that, in some circumstances, it might be significantly more expensive to construct the object and then set public properties vs. passing the values through the constructor itself. (You may know that this is not the case, but as the consumer of a class, you shouldn't presume to know care about the implementation details, because they are subject to change).
Your code won't break if one or more of those properties have their names changed, or become read-only (which the ID
probably should have been in the first place, but perhaps wasn't due to architectural constraints like that of an ORM).
However, there is one case where you have to use initializers instead of overloaded constructors, and that is when chaining selects in a Linq to SQL/EF query:
var bars =
from f in ctx.Foo
select new Bar { X = f.X, Y = f.Y };
var bazzes =
from b in bars
select new Baz { ... };
This can actually fail with a "no supported mapping" if you use constructor overloads instead of default constructors + initializers. This is, however, a constraint of the technology being used (and an undesirable one at that), and not a coding style issue.
In other cases, you should prefer the constructor overload over the initializer.
If there is no useful/relevant constructor overload that can do the same thing as your initializer, then go ahead and write the initializer, there's nothing wrong with it. The feature exists for a good reason - it makes the code easier to write and read.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With