2009-05-14 4 views
27

Мне было интересно, какой предпочтительный способ построить новый объект в C#?Каков предпочтительный способ построения объектов в C#? Параметры или свойства конструктора?

Возьмите класс Person:

public class Person 
{ 
    private string name; 
    private int age; 

    //Omitted.. 
} 

Должен ли я создать его использовать:

New Person("name", 24); 

или

New Person() { Name = "name", Age = 24 }; 

Является ли это просто дело вкуса или есть хороший повод использовать один над другим?

Я могу себе представить, что нужно использовать только необходимые поля в конструкторе и необязательные поля не как параметры конструктора, а с помощью свойств.

Я в порядке?

+2

Это фактически версия C# http://stackoverflow.com/questions/830657 –

ответ

39

Предпочтительный способ зависит от вашего дизайна.

Свойства конструктора предназначены для элементов, которые требуется для вашего объекта, чтобы они были правильно построены. То есть любые свойства, которые должен иметь объект для инициализации, должны быть в конструкторе (обычно вы не хотите, чтобы объект с частичным intialized после вызова конструктора, если вы не создаете шаблон фабрики или строителя, и конструктор скрыт от всех, кроме фабрики/строителя).

Имущественные инициализаторы лучше всего подходят для дополнительной конфигурации после конструктора, требуемого вашим конкретным вариантом использования, но не требуются для того, чтобы объект считался инициализированным.

Например, у вас может быть объект, который представляет человека. Человеку требуется имя и возраст для инициализации, но адрес, на котором они живут, является дополнительной конфигурацией.Таким образом, имя и возраст - это параметры конструктора, а адрес - свойство чтения/записи.

Person johnDoe = new Person("John Doe", 24) { Address = "42 Adams Street" }; 
+0

Это очень хорошее объяснение, которое помогает распознать, когда применять что. Благодаря! – Peterdk

+0

Я также рекомендую прочитать ответ Марка Гравелла (http://stackoverflow.com/a/863064/23234), в котором приводятся некоторые альтернативные сценарии, которые рассматривают реальное использование, когда более идеальная точка обзора, представленная мной, не имеет значения из-за других ограничений. –

2

Второй способ это просто синтаксический сахар для установки свойств вручную:

Person p = new Person(); 
p.Name = "name"; 
p.Age = 24; 

Вы также не в зависимости от конструктора, который не может инициализировать все свойства, которые вы хотите установить.

Если у вашего класса есть конструктор, который требует этих двух параметров, вы не сможете явно вызвать этот конструктор.

12

Это действительно зависит от сценария. Метод собственности имеет большое удобство, поскольку вам не нужно дублировать назначения в конструкторе. Более того, большинство сценариев привязки данных, например, могут создавать новые объекты, для которых они обычно используют конструктор без параметров, поэтому это большое преимущество.

Однако для неизменяемых типов подход конструктора является единственным разумным вариантом. Интересно (возможно), что именованные/необязательные параметры в C# 4.0 позволяют что-то похожее на инициализаторы объектов для неизменяемых типов - see here.

Подход конструктора также очень популярен для инфраструктур инверсии управления, поскольку он четко рекламирует, что нужно для этого класса.

Возможно, вам нужно будет смешивать и сопоставлять, но обычно больше свойства, чем стиль конструктора.

+2

Да - поля только для чтения могут быть установлены только кодом конструктора. – Dario

5

Установка значений в конструкторе делает эти свойства обязательными, поэтому это означает, что вы не можете создать новый экземпляр без настроек этих свойств. В некоторых ситуациях это предпочтительнее, в других ситуациях это нецелесообразно.

+0

Это правда, хотя для структур вы не можете, конечно, сделать установку обязательной. – Noldorin

+0

Действительно, это потому, что структура является типом значений и всегда имеет значение (по умолчанию), даже если оно не инициализировано. –

0

Мои основные соображения по этому вопросу: 1) Сколько данных будет иметь экземпляр объекта при создании объекта и 2) Каким должен быть инкапсулированный класс? Если после установки свойства не должны меняться (по крайней мере, из-за каких-либо внешних объектов), я бы использовал конструктор, поскольку конструктор может устанавливать любые свойства только для чтения, а также любые свойства чтения/записи.

Также помните, что, как и любой другой метод, ваш конструктор может быть перегружен, поэтому вы можете настроить любое количество способов инициализации этих свойств.

В общем, я использую конструкторы, но иногда я устанавливаю свойства вручную вместо этого. Используйте правильный инструмент для правильной проблемы.

7

Я всегда работаю на основе того, что вы должны передавать значения конструктору, которые являются обязательными для существования этого объекта в допустимом состоянии.

В вашем примере вы могли бы сказать, что новый человек не может существовать без возраста, поэтому он должен быть передан конструктору.

Если вы работаете на основе того, что объект должен моделировать реальный рабочий объект, то это определяет минимальное значение, необходимое для того, чтобы любой объект был действительным - независимо от того, заданы ли они как значения по умолчанию или передаются значения через конструктор.

+1

+1: Объекты всегда должны оставаться в допустимом состоянии после построения и после каждого вызова метода, поэтому методы не должны проверять состояние своего собственного объекта перед запуском, а только их аргументы. – palm3D

2

На мой взгляд, вы должны сначала решить, что делает человека человеком. Каковы свойства человека для того, чтобы объект-объект был правильно создан. Каковы минимальные требования для человека.

Всегда должен быть конструктор с минимальными требованиями.

Я бы использовал только инициализаторы объектов для типов, для которых не требуются какие-либо свойства, которые необходимо создать. Следовательно, конструктор по умолчанию.

Я знаю, что инициализаторы объектов очень удобны. Другим механизмам также может потребоваться пустой конструктор объекта.

Но я не думаю, что это имеет большой смысл, что вы можете создать Личность без имени.

4

Несколько мыслей:

  1. Вы должны общие свойства для использования Инициализаторов объектов. Поэтому, если есть что-то, чего вы не хотите раскрывать, вы должны инициализировать их по параметру конструктора.

  2. Если вы проверили IL, вы обнаружите, что Object Initializer не является «атомарным». Если вы пишете код, как это (не то, что я рекомендую, просто пример):

    using (p = New Person() {Name = GetName(), Age = GetAge()}) 
    { 
        //blah, blah 
    } 
    

    Если есть исключение в GetAge(), вы будете создавать экземпляр Person в поврежденном состоянии. Хуже того, вы никогда не сможете войти в область использования, и этот экземпляр не будет удален, как вы себе представляли.

+0

Мне нравится, что вы указали, что это не атомный. Очевидно, когда вы это видите, и я не думаю, что буду писать код, как вы его выразили, но если бы я читал чей-то код, я бы не подумал, что я бы взял его. –

0

Для настройки свойств вручную они должны быть объявлены общедоступными, и вы можете захотеть, чтобы члены класса были закрытыми. В этом случае конструктор - это способ пойти или написать методы, чтобы получить/установить их или использовать аксессор.

0

Я предпочитаю очень простой конструктор с функциями intializers. Я нахожу, что это приводит к более явному коду. Единственные данные, которые я передал бы конструктору, это информация, которую я не хочу, чтобы пользователь моего класса изменялся после создания класса.

0

Настоящая проблема возникает, когда вы передаете объект через уровни/слои. Например, вы создаете объект человека и проходите через веб-сервис. Webservice в некоторой точке пытается десериализовать и пытается создать экземпляр, а затем вы можете получить сообщение об ошибке, если для конструкторов требуется параметр! поскольку веб-службы сначала создают объект, а затем присваивают значения.

Так что я бы предпочел беззазорный конструктор, если он представляет собой datamodel (тип POCO), который должен пройти через уровни.

По другим причинам параметр contructor является лучшим способом (для обязательных полей). Особенно при предоставлении класса как итератора внешним объектам или узлам.