什么'对象初始化器和构造函数之间的区别是什么?

两者之间的区别是什么?什么时候使用“对象初始化器”而不是“构造函数”,反之亦然?我正在使用C#,如果这很重要的话。另外,对象初始化器方法是特定于C#还是.NET的?

95076 次浏览

A constructor is a defined method on a type which takes a specified number of parameters and is used to create and initialize an object.

An object initializer is code that runs on an object after a constructor and can be used to succinctly set any number of fields on the object to specified values. The setting of these fields occurs after the constructor is called.

You would use a constructor without the help of an object initializer if the constructor sufficiently set the initial state of the object. An object initializer however must be used in conjunction with a constructor. The syntax requires the explicit or implicit use (VB.Net and C#) of a constructor to create the initial object. You would use an object initializer when the constructor does not sufficiently initialize the object to your use and a few simple field and/or property sets would.

A constructor is a method (possibly) accepting parameters and returning a new instance of a class. It may contain initialization logic. Below you can see an example of a constructor.


public class Foo
{
private SomeClass s;
public Foo(string s)
{
s = new SomeClass(s);
}
}

Now consider the following example:


public class Foo
{
public SomeClass s { get; set; }
public Foo() {}
}

You could achieve the same result as in the first example using an object initializer, assuming that you can access SomeClass, with the following code:


new Foo() { s = new SomeClass(someString) }

As you can see, an object initializer allows you to specify values for public fields and public (settable) properties at the same time construction is performed, and that's especially useful when the constructor doesn't supply any overload initializing certain fields. Please mind, however that object initializers are just syntactic sugar and that after compilation won't really differ from a sequence of assignments.

If you have properties that MUST be set on your object for it to work properly, one way is to expose just a single constructor which requires those mandatory properties as parameters.

In that case, you cannot create your object without specifying those mandatory properties. Something like that cannot be enforced by object initializers.

Object initializers are really just a "syntax convenience" to shorten initial assignments. Nice, but not really very functionally relevant.

Marc

Object Initializers were something added to C# 3, in order to simplify construction of objects when you're using an object.

Constructors run, given 0 or more parameters, and are used to create and initialize an object before the calling method gets the handle to the created object. For example:

MyObject myObjectInstance = new MyObject(param1, param2);

In this case, the constructor of MyObject will be run with the values param1 and param2. These are both used to create the new MyObject in memory. The created object (which is setup using those parameters) gets returned, and set to myObjectInstance.

In general, it's considered good practice to have a constructor require the parameters needed in order to completely setup an object, so that it's impossible to create an object in an invalid state.

However, there are often "extra" properties that could be set, but are not required. This could be handled through overloaded constructors, but leads to having lots of constructors that aren't necessarily useful in the majority of circumstances.

This leads to object initializers - An Object Initializer lets you set properties or fields on your object after it's been constructed, but before you can use it by anything else. For example:

MyObject myObjectInstance = new MyObject(param1, param2)
{
MyProperty = someUsefulValue
};

This will behave about the same as if you do this:

MyObject myObjectInstance = new MyObject(param1, param2);
myObjectInstance.MyProperty = someUsefulValue;

However, in multi-threaded environments the atomicity of the object initializer may be beneficial, since it prevents the object from being in a not-fully initialized state (see this answer for more details) - it's either null or initialized like you intended.

Also, object initializers are simpler to read (especially when you set multiple values), so they give you the same benefit as many overloads on the constructor, without the need to have many overloads complicating the API for that class.

When you do

Person p = new Person { Name = "a", Age = 23 };

this is what an object initializer essentially does:

Person tmp = new Person(); //creates temp object calling default constructor
tmp.Name = "a";
tmp.Age = 23;
p = tmp;

Now this facilitates behaviour like this. Knowing how object initializers work is important.

Object initializers are especially useful in LINQ query expressions. Query expressions make frequent use of anonymous types, which can only be initialized by using an object initializer, as shown in the code example below:`

var orderLineReceiver = new { ReceiverName = "Name Surname", ReceiverAddress = "Some address" };

More about it - Object and collection initializers

Object initializers can be useful to initialize some small collection which can be used for testing purposes in the initial program creation stage. The code example is below:

    class Program
{
static void Main(string[] args)
{
List<OrderLine> ordersLines = new List<OrderLine>()
{
new OrderLine {Platform = "AmazonUK", OrderId = "200-2255555-3000012", ItemTitle = "Test product 1"},
new OrderLine {Platform = "AmazonUK", OrderId = "200-2255555-3000013", ItemTitle = "Test product 2"},
new OrderLine {Platform  = "AmazonUK", OrderId = "200-2255555-3000013", ItemTitle = "Test product 3"}
};
}
}
class OrderLine
{
public string Platform { get; set; }
public string OrderId { get; set; }
public string ItemTitle { get; set; }
}

Here is the catch. In the above code example isn’t included any constructor and it works correctly, but if some constructor with parameters will be included in the OrderLine class as example:

public OrderLine(string platform, string orderId, string itemTitle)
{
Platform = platform;
OrderId = orderId;
ItemTitle = itemTitle;
}

The compiler will show error - There is no argument given that corresponds to the required formal parameter…. It can be fixed by including in the OrderLine class explicit default constructor without parameters:

public OrderLine() {}