Чи завжди ми повинні включати конструктор за замовчуванням до класу?


76

Мені задало це питання колега, чи завжди ми повинні включати конструктор за замовчуванням у клас? Якщо так, то чому? Якщо ні, чому ні?

Приклад

public class Foo {

    Foo() { }

    Foo(int x, int y) {
        ...
    } 

}

Мені також цікаво дізнатись про це від експертів.


20
Як завжди - лише тоді, коли це необхідно.
Арніс Лапса,

Відповіді:


121

Ви повинні пам’ятати, що якщо ви не надасте перевантажений конструктор, компілятор створить для вас конструктор за замовчуванням. Це означає, що якщо у вас просто є

public class Foo
{ 
} 

Компілятор генерує це як:

public class Foo
{ 
    public Foo() { }  
} 

Однак, як тільки ви додасте інший конструктор

public class Foo
{ 
    public Foo(int x, int y)
    { 
        // ... 
    }  
} 

Компілятор більше не буде автоматично генерувати конструктор за замовчуванням для вас. Якби клас вже використовувався в іншому коді, який покладався на наявність конструктора за замовчуванням Foo f = new Foo();, цей код тепер би зламався.

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

Однак бувають випадки, коли необхідно надати конструктор за замовчуванням (будь то загальнодоступний чи приватний). Як вже згадувалося раніше, для деяких типів серіалізації потрібен конструктор за замовчуванням. Бувають також випадки, коли клас має кілька параметризованих конструкторів, але також вимагає ініціалізації "нижчого рівня", і в цьому випадку може бути використаний приватний конструктор за замовчуванням, який прив’язаний до параметризованих конструкторів.

public class Foo
{
   private Foo()
   {
      // do some low level initialization here
   }

   public Foo(int x, int y)
      : this()
   {
      // ...
   }

   public Foo(int x, int y, int z)
      : this()
   {
      // ...
   }
}

Думаю, вам не вистачає там кількох publicмодифікаторів (учасники класу privateза замовчуванням) ...
Дан Тао,

@ Дан Тао: Так, я був. Дякую @Anthony Pegram за додавання їх для мене.
Скотт Дорман,

1
@ Джон Ханна: Виправлено () після назв класів. Однак ваш коментар щодо неправильної відповіді - це ... ну, неправильно. Перший код буде скомпільований і дозволить вам писати такий код, як Foo f = new Foo();. Однак, як тільки ви зміните клас на той, що показаний у третьому прикладі, Foo f = new Foo();це спричинить помилку компілятора - що було моєю думкою. Поки у вас немає конструкторів, компілятор додає конструктор за замовчуванням для вас. Як тільки ви додаєте будь-який інший конструктор, компілятор більше не додає конструктор за замовчуванням і код, який покладався на його наявність, розриви.
Скотт Дорман,

2
Поставити +1 цьому. Створення порожніх конструкторів за замовчуванням дратує IMO, і такі інструменти, як ReSharper, просто позначають його як мертвий код.
womp

1
@ Скотт. Так, просто неправильно прочитане сканування занадто швидко. Моє ліжко.
Джон Ханна,

19

Деякі речі (наприклад, серіалізація) вимагають конструктора за замовчуванням. Однак поза цим, конструктор за замовчуванням слід додавати лише в тому випадку, якщо це має сенс.

Наприклад, якщо Foo.Xі Foo.Yвластивості незмінні після будівництва , то конструктор за замовчуванням не має сенсу. Навіть якби він використовувався для `` порожнього '' Foo, статичний Emptyаксесуар був би більш відкритим.


3
Серіалізація не вимагає конструктора за замовчуванням. Якщо у вас є клас, який ви хочете мати серіалізацію, для якого конструктор за замовчуванням не має сенсу, тоді реалізуйте ISerializableта додайте конструктор ( privateякщо запечатаний, protectedінакше) з підписом, ClassName(SerializationInfo info, StreamingContext context)який відображає реалізацію ISerializable.GetObjectData.
Джон Ханна,

12

Я б сказав, що ні , точно не завжди . Припустимо, у вас є клас із деякими полями лише для читання, які потрібно ініціалізувати до якогось значення, і немає розумних за замовчуванням (або ви не хочете, щоб це було)? У цьому сценарії я не думаю, що конструктор без параметрів має сенс.


4
Повністю згоден. Класи завжди повинні бути створені у правильному стані. Якщо у вас є ctor за замовчуванням і встановлені властивості після побудови, коли екземпляр дійсний? Після налаштувань деякі властивості? Після встановлення всіх? Хто знає.
Кумбая,

6

Наявність конструктора за замовчуванням - це лише гарна ідея, якщо є сенс мати такий об’єкт.

Якщо ви створюєте об'єкт, який не в допустимому стані, із такого конструктора, тоді єдине, що він може зробити, - це ввести помилку.


2

Так, краще мати конструктор за замовчуванням, щоб уникнути плутанини. Я бачив, як люди просто нічого не роблять у конструкторі за замовчуванням (навіть у власних класах Microsoft), але все одно люблять зберігати це, оскільки об'єкти отримують значення за замовчуванням (тип) автоматично. Класи, в яких не вказаний конструктор за замовчуванням, .NET автоматично додасть їх для вас.

Серіалізація потребує конструктора за замовчуванням, якщо ви використовуєте існуючі серіалізатори, оскільки це має сенс для серіалізаторів загального призначення, інакше вам потрібно створити власну реалізацію.


1
Серіалізація не вимагає конструктора за замовчуванням. Якщо у вас є клас, який ви хочете мати серіалізацію, для якого конструктор за замовчуванням не має сенсу, тоді реалізуйте ISerializableта додайте конструктор ( privateякщо запечатаний, protectedінакше) з підписом, ClassName(SerializationInfo info, StreamingContext context)який відображає реалізацію ISerializable.GetObjectData.
Джон Ханна,

Очко зараховано. Насправді я це знаю, але загалом люди хочуть бути простими. Але ви додали бал до відповіді. Дякую.
abhishek

1

Як побічне зауваження, використовуючи struct замість класу, зауважте, що неможливо залишити конструктор за замовчуванням, а також неможливо визначити його самостійно, тому незалежно від того, які конструктори ви визначаєте, переконайтеся, що стан за замовчуванням struct (коли всі змінні встановлені в стан за замовчуванням (зазвичай 0 для типів значень, і null для посилальних типів) не порушить вашу реалізацію struct.


0

У більшості випадків конструктор за замовчуванням є хорошою ідеєю. Але оскільки ви використовуєте слово "завжди", потрібен лише один приклад. Якщо ви подивитесь на структуру, ви знайдете багато. Наприклад, System.Web.HttpContext.


0

Загальний тип можна створити за допомогою засобів C # (без відображення), лише якщо він має конструктор за замовчуванням. Крім того, new()має бути вказано обмеження загального типу:

void Construct<T>()
    where T : new()
{
    var t = new T();
    ...
}

Виклик цього методу з використанням типу як загального аргументу типу, який не має конструктора за замовчуванням, призводить до помилки компілятора.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.