System.String - почему он не реализует конструктор без параметров?

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

Из-за этого вы не можете использовать этот тип при наличии ограничения new ().

ОБНОВЛЕНИЕ

Как вы думаете, почему новая строка () бесполезна и, например, new int () не бесполезен. Я действительно не вижу разницы. Я действительно хотел бы иметь возможность использовать ограничение new () для строки, поскольку я ожидаю String.Empty.

ОБНОВЛЕНИЕ2

Я знаю, что int - это тип значения, и все типы значений имеют конструктор без параметров по умолчанию. Я имел в виду ответ @John Saunders (и аналогичные ответы), в котором говорилось, что новый String () в основном не делает ничего полезного. Итак, если new String () не означает ничего полезного, что такого полезного в new int ()? Я также понимаю, что для типов значений важно иметь конструкторы без параметров по умолчанию. Более того, я думаю, что для строки было бы действительно хорошо иметь конструктор без параметров по умолчанию. Отсутствие конструктора без параметров означает для меня, что объект просто не может существовать без параметра, который будет его инициализировать. На мой взгляд, строка не нуждается в параметрах, потому что она может быть пустой. Я задал этот вопрос, потому что хотел узнать, есть ли какая-то важная причина, по которой строка не имеет конструктора без параметров, или это просто дизайн или что-то еще.

ОБНОВЛЕНИЕ3

Думаю, это будет последнее обновление, так как оно больше похоже на сообщение в блоге, чем на вопрос. В java строки также неизменяемы, и new String () совершенно законен. Однако в документации для конструктора без параметров говорится:

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

ОБНОВЛЕНИЕ4

Хорошо, последнее обновление;) Я должен согласиться с Джоном Сондерсом. Я использовал код с ограничением new (), и он отлично работал с пользовательскими классами. Затем моему коллеге нужно было изменить код для целых чисел, и все было в порядке. Затем замените его на строку, и у нас возникла проблема. Теперь, когда я думаю об этом, я думаю, что код, который мы используем, нужно изменить, чтобы отразить, что нам нужны скалярные типы, а не классы с конструктором без параметров. С учетом всего сказанного, я все еще считаю немного несогласованным, что вы можете написать new int () и вы не можете написать new string () (да, я понимаю, что int - это тип значения;)). Спасибо за все ваши ответы!

Заранее благодарим за помощь.


person empi    schedule 16.03.2010    source источник
comment
Добавлены ограничения тегов, потому что в этом суть проблемы.   -  person Hans Kesting    schedule 16.03.2010
comment
Можете ли вы распечатать исходный код для случая использования, когда вам нужно, чтобы строка была передана в универсальный метод с новым ограничением. Я думаю, это может прояснить ситуацию.   -  person Iain Galloway    schedule 16.03.2010
comment
Это не только новое ограничение, но и конструктор по умолчанию, который также упрощает создание пустых экземпляров строк с отражением.   -  person Juliet    schedule 16.03.2010
comment
Я бы сказал, что это дизайнерское решение команды CLR, на которое должен ответить кто-то из них или кто-то, кто знает, на чем они основаны. Я предполагаю, что они не хотят, чтобы вы думали, что строка является ссылочным типом, с которым, как сказал Джон Сондерс, можно действовать, как если бы это ссылка на экземпляр.   -  person Chris S    schedule 16.03.2010
comment
@empi: Пожалуйста, покажите мне, что бы вы сделали с такой пустой строкой в ​​контексте Reflection или Generic. Я не могу придумать никакого значения для такой пустой строки. Кстати, я не имел в виду, что конструктор бесполезен - я имел в виду, что результирующая строка будет бесполезной.   -  person John Saunders    schedule 16.03.2010
comment
Даже перечисления могут быть созданы как new MyEnum(), но не строки!   -  person nawfal    schedule 23.04.2013


Ответы (11)


Если бы вы могли использовать

string s = new string();

Что бы вы с ним сделали? Строки неизменны.


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

Не забывайте, что ОП сказал, что хочет сделать:

public void SomeMethod<T>() where T : new()
{
    var t = new T();
    // Do _what_ now?
}

// Call:
SomeMethod<string>();

Я не понимаю, что вы могли сделать с t после того, как он был построен.

person John Saunders    schedule 16.03.2010
comment
Значит, вы никогда не используете String.Empty? - person JoeG; 16.03.2010
comment
@Joe Gauterin - Я не думаю, что Сондерс говорит что-либо о String.Empty. Он говорит, что не имеет смысла иметь конструктор без параметров для строкового класса. Либо вы устанавливаете значение по умолчанию, string.empty или null. - person JonH; 16.03.2010
comment
@John: new String () должен разумно возвращать String.Empty. Меня несколько раз сжигали, потому что я хотел передать строку в общий метод DoSomething<T>(T t) where T : new(), но я не могу работать со строками, потому что у них нет конструктора по умолчанию. - person Juliet; 16.03.2010
comment
@JonH: Очевидно, имеет смысл иметь конструктор без параметров, поскольку у спрашивающего есть вариант использования для него. Не помогает то, что значение по умолчанию для всех типов классов равно null, а не String.Empty. - person JoeG; 16.03.2010
comment
@ Джульетта: Почему? Это создало бы множество пустых строк для сбора мусора, поскольку это создало бы новый экземпляр строки только для того, чтобы иметь пустую строку. Я бы предпочел один экземпляр, который означал пустую строку, чем много просто для удобства использования where T : new(). Если вам нужен тип string, обладающий такой способностью, создайте свою собственную оболочку или используйте вместо этого StringBuilder. Квадратный колышек, круглое отверстие - не долбивайте колышек. - person Jeff Yates; 16.03.2010
comment
На мой взгляд, если есть new int (), нет причин для отсутствия новой строки () - person empi; 16.03.2010
comment
@Jeff: если я не хочу создавать много пустых строк, я создам один экземпляр и кэширую его локально в моем методе. Чтобы назвать один пример, мне нужно было написать код для моей компании, который создает новый объект и инициализирует весь его граф объектов ненулевым значением. Каждый объект в графе гарантированно имел конструкторы по умолчанию, за исключением строк, которые обрабатывались как особый случай. Microsoft считает, что String.Empty избавляет разработчика от самих себя на тот случай, если они неоднократно обновляют пустые строки, но конечный результат фактически повредил дизайну моего кода. - person Juliet; 16.03.2010
comment
@ Джульетта: Но легко написать собственную структуру, которая обертывает строку и делает то, что вы хотите. Где хлопот? Я думаю, что фреймворк оправданно ограничивает построение строк таким образом, возлагая на разработчика ответственность за написание оболочки, если они хотят ее обойти. - person Jeff Yates; 16.03.2010
comment
@empi: int - это тип значения. Строки - это особый тип ссылки. Они очень разные. - person Jeff Yates; 16.03.2010
comment
Я проголосовал против, потому что это не ответ, а комментарий к вопросу. - person Joren; 16.03.2010
comment
@Joren: нет, это ответ. у него нет конструктора без параметров, потому что он не имеет смысла в неизменяемом типе. - person John Saunders; 16.03.2010
comment
@ Джульетта: это чертовски странный вариант использования. Для чего это? Безопасность? Вы знаете о классе SecureString? У него есть конструктор без параметров. - person John Saunders; 16.03.2010
comment
@John: приведенный выше вариант использования помог с тестированием: у нас было много классов, которые отображали объекты типа A в тип B. Мы создали новый экземпляр A с заполненным графом объектов, сопоставили его с B, и если B имел любые пустые поля, что-то не было скопировано, поэтому тест не пройден. Без заполнения графа объектов невозможно отличить копирование нуля из A в B от полного отсутствия копирования из A в B. - person Juliet; 17.03.2010
comment
Я не понимаю, что вы могли бы делать с t после того, как он был построен. - имеет ли значение, представляет ли T строку или тип, отличный от BCL? Нет, поэтому вы используете его так же, как любой другой метод с новым ограничением. Вы можете вернуть тип T, назначить его свойству, добавить ограничение, которое T реализует IComparable, создать новый объект в случае, если null является недопустимым значением и т. Д. Мне просто трудно увидеть аргумент, который конструкторы строк по умолчанию никогда не иметь цели. - person Juliet; 17.03.2010
comment
@Juliet: Ваш пример модульного теста довольно неясен: вы хотите доказать, что ваш метод сопоставления копирует что-то ненулевое, поэтому вам нужно, чтобы каждое свойство было ненулевым, поэтому вы устанавливаете каждое свойство равным результату вызова его конструктора по умолчанию. Итак, ответ на мой вопрос о том, что вы собираетесь с ним делать, - сравнить его с нулевым значением. Вы пробовали установить для каждого свойства значение default(T)? - person John Saunders; 17.03.2010
comment
Просто чтобы вы знали, я снял свой голос против, потому что думаю, вы добавили достаточно, чтобы ответ был стоящим. - person Joren; 17.03.2010
comment
I don't see what you could do with t after it was constructed добавьте его в List<T> для начала. Например: List<T> makeDefaultArray<T>(int size); Да, можно сделать List<T> makeFilledArray<T>(int size, T val);, но при этом каждый раз используется одна и та же ссылка на объект. Что, если мне нужны разные предметы? Со строками это не имеет значения, но разработчик makeDefaultArray этого не знает, и не нужно беспокоиться об этом. Отсутствие String() в C # является серьезным упущением при написании универсального кода. - person Thomas Eding; 28.11.2012
comment
@ThomasEding: что бы вы сделали с пустыми или заполненными val строками в списке? Вы не можете их изменить! - person John Saunders; 28.11.2012
comment
@JohnSaunders: Предположительно, только сайт вызова знает, что это строка. Универсальная функция - нет. - person Thomas Eding; 28.11.2012
comment
@ThomasEding: ты не ответил на мой вопрос. Что бы вы сделали с ним на месте звонка? - person John Saunders; 28.11.2012
comment
@JohnSaunders: легко создавать список пустых строк из общего кода. Что мне теперь нужно от списка пустых строк? Для этого не потребуется много воображения. Я мог бы добавить его в какой-нибудь другой список. Я мог бы добавить к нему строки. Я мог бы выполнить на нем какой-нибудь код в функциональном стиле. Моя непосредственная причина посетить этот вопрос заключается в том, что я компилирую код C ++ в C #. Преобразование T arr[N]; может компилироваться в код типа T[] arr = makeDefaultArray<T>(N); Но, увы, это не сработает, если T равно String. Так что я должен сделать что-то вроде T[] arr = replicate<T>(N, ""); особого случая по очень небольшой причине. - person Thomas Eding; 28.11.2012
comment
@JohnSaunders Все твои аргументы бессмысленны. OP обновился, Джульетта и Эдинг прокомментировали, где new T() имеет смысл, даже если T является строкой. И какое отношение имеет неизменность к ключевому слову new? Почти все неизменяемые типы в BCL имеют new. - person nawfal; 22.04.2013

Как вы думаете, почему новая строка () бесполезна и, например, new int () не бесполезен.

Вернемся к основам и поговорим о ссылочных типах и типах значений.

Когда вы создаете int, long, point или что-то еще, тип значения выделяется в стеке, поэтому int резервирует 4 байта в стеке, long - 8 байтов и так далее. Пространство стека, зарезервированное для типа значения, обнулено - невозможно когда-либо сослаться на адрес типа значения в стеке и получить нуль. Таким образом, конструкторы по умолчанию для типов значений, вероятно, являются результатом скорее причуд при распределении стека, чем полезности (возможно, кто-то в MS может дать больше информации).

В любом случае я думаю, что System.String не имеет конструктора по умолчанию из-за паршивого дизайна API.

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

Я думаю, что у Microsoft были добрые намерения: лучше использовать "" или String.Empty всякий раз, когда вам нужна пустая строка, потому что они интернированы, и не так полезно каждый раз обновлять new String() - но это встречается для меня как способ Microsoft «спасти разработчиков от самих себя».

(Спасибо, Microsoft, я ценю вашу патерналистскую поддержку, но я вполне способен позаботиться о себе. Если я не хочу создавать несколько экземпляров одного и того же объекта, я кэширую его так же, как и для bagillion других типов с конструктором по умолчанию.)

На данный момент вам нужно рассматривать объекты без конструкторов по умолчанию как особый случай из всего остального. Переписать

public void DoSomething<T>() where T : new() { ... }

Как два метода:

public void DoSomething<T>() where T : new() { DoSomething(() => new T()); }

public void DoSomething<T>(Func<T> constructor) { }

Клиенты вашего кода должны решить, какую функцию вызывать для себя.


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

public void DoSomething<T>(T obj) { ... }

Могут быть причины, например, если не гарантируется вызов метода конструктора. Я работал со сложным приложением winform, в котором при нажатии кнопок или пунктов меню всплывала форма - или, если форма уже была открыта, мы вместо этого фокусировали ее. Так получилось что-то вроде:

public T ShowForm<T>(Func<T> constructor) where T : Form
{
    T res = default(T);
    foreach(Form f in Application.OpenForms)
    {
        if (f is T)
        {
            res = (T)f;
            break;
        }
    }

    if (res == null)
        res = constructor();

    res.Focus();
    return res;
}

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

person Juliet    schedule 16.03.2010
comment
+1, красивое решение. Однако он работает, только если T является параметром типа для универсального метода, а не для универсального класса. - person Thomas Levesque; 16.03.2010
comment
Вы, безусловно, можете использовать его для общих классов: DoSomething(() => new List<int>()); - person Juliet; 17.03.2010

Я предполагаю, что вы хотите определить класс примерно так:

public class SpecializedCollection<T> where T : new() {
    private List<T> _startingItems;

    public SpecializedCollection(int startingSize) {
        _startingItems = new List<T>(startingSize);
        for (int i = 0; i < startingSize; ++i)
            _startingItems.Add(new T());
    }

    // ... lots more code
}

Я прав? Если вы хотите, чтобы это было более гибким, чтобы разрешить типы, которые могут не иметь конструктора без параметров по умолчанию, вы всегда можете сделать это:

public class SpecializedCollection<T> {
    private List<T> _startingItems;

    // note: here we leave it up to the caller to specify
    // how T objects will be instantiated
    public SpecializedCollection(int startingSize, Func<T> generator) {
        _startingItems = new List<T>(startingSize);
        for (int i = 0; i < startingSize; ++i)
            _startingItems.Add(generator.Invoke());
    }
}

Тогда вы можете создать SpecializedCollection<string> вот так:

var strings = new SpecializedCollection<string>(10, () => string.Empty);

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

public SpecializedCollection(int startingSize)
    : this(startingSize, Activator.CreateInstance<T>()) { }

Вышеупомянутое, конечно, не сработало бы для string, но сработало бы для любого класса с конструктором без параметров (т. Е. Для всего, что работало бы со старым ограничением where T : new()).

person Dan Tao    schedule 16.03.2010

Что такое строка без реальной строки?

Ты можешь это сделать:

  String s = "hello world";

И как только у вас есть строка, вы не можете изменить эту конкретную строку (strimgs неизменны). Если вы хотите объявить пустую строку, просто выполните:

string s = string.empty
person JonH    schedule 16.03.2010

Это обычная проблема в универсальном коде. C ++ решает эту проблему с помощью специализации шаблонов, но C # не поддерживает это.

Однако это не может быть сделано чисто на C # - лучше всего использовать вместо этого ограничение class, а затем использовать хак на основе отражения для построения по умолчанию T или использовать String.Empty, если T равно System.String. Вы потеряете проверку компилятора на использование других классов, которые, однако, не могут быть сконструированы по умолчанию.

Вот еще вариант:

class MyClass<T> where T : class
{
    public MyClass(T default)
    private T m_default;      
};

MyClass<object> instance = new MyClass<object>(new object());
MyClass<string> instance = new MyClass<string>(String.Empty);

И другой:

class MyClass<T> where T : class
{
    static SetDefault(T def)
    { 
       s_default = def;
    }
    static T s_default;      
};

MyClass<object> instance = new MyClass<object>(new object());
MyClass<string> instance = new MyClass<string>(String.Empty);
person JoeG    schedule 16.03.2010

«[Почему] System.String ... не содержит [] конструктора без параметров?»

Потому что так оно и было задумано. Как вы можете видеть из множества ответов и мнений здесь, нет даже четкого консенсуса относительно необходимости или реализации конструктора без параметров. Очевидно, технически возможно иметь конструктор по умолчанию, который инициализируется значением String.Empty - так что либо команда BCL не подумала об этом (маловероятно), либо не сочла это стоящим сценарием (вероятно). FWIW, я согласен - я не вижу причин, по которым ctor по умолчанию на string важен.

«Как вы думаете, почему new string () бесполезен и, например, new int () не бесполезен. Я действительно не вижу разницы».

Разница в том, что int - это ValueType. ValueTypes требуется для использования по умолчанию конструктор, и он инициализирует тип значением по умолчанию. string является ссылочным типом - он уже имеет значение по умолчанию - null (такое же, как и все другие ссылочные типы).

Конечно, вы не согласны - у вас есть конкретный сценарий, который, по вашему мнению, лучше всего реализовать с помощью ctor по умолчанию на string. Не зная подробностей своего сценария, сложно его разумно прокомментировать. Но я предполагаю, что вы имеете дело с примитивной одержимостью. Если string.Empty имеет смысл для ваших конкретных данных, то вам, вероятно, следует создать для этого класс. Конструктор по умолчанию может инициализировать string.Empty.

Э: Почему примитивная одержимость?

Я собирался написать об ORM или других картографах (несколько мест, которые я могу придумать, что where T:new() полезно) и о том, как вместо попытки сопоставить strings вы должны использовать собственный бизнес-объект. Но, выполнив некоторый пример кода - я довольно застрял. Я не могу придумать ни одного сценария, который имел бы смысл с new string() == string.Empty.

person Mark Brackett    schedule 16.03.2010
comment
Примитивная одержимость означает, что вы создаете такой метод, как PersistCustomer(customerId, customerFirstName, customerLastName, customerPhone) вместо PersistCustomer(customer). Я не думаю, что конструкторы по умолчанию для объектов считаются примитивной одержимостью. - person Juliet; 16.03.2010
comment
@Juliet - Да, это пример примитивной одержимости - спасибо. ;) Более широкая картина заключается в том, что вы пытаетесь навязать значение примитивам (о, посмотрите, это int с именем customerId - должно быть, это ссылка на клиента!). Я обновлю немного больше о почему Я предполагаю, что OP делает это ... (обратите внимание, что это предположение. Я не знаю, почему он хочет использовать значение по умолчанию ctor) - person Mark Brackett; 16.03.2010
comment
Поскольку типы значений требуются для наличия конструкторов по умолчанию, почему бы не require String иметь конструктор по умолчанию? То, что вы представляете, не является хорошим аргументом. - person Thomas Eding; 28.11.2012
comment
@ThomasEding - AFAIK, ValueTypes должны иметь неявный ctor по умолчанию по техническим причинам, а не для вашего удобства. Я не имел в виду, что MS требует ctors по умолчанию, чтобы вы могли использовать их в новом ограничении. Просто так получается, что иногда полезно, иногда нет. - person Mark Brackett; 28.11.2012

Конкретно обращаясь к тому, как избежать проблемы ограничения new() при работе с универсальными типами:

Включите следующее поле, свойство и метод:

private readonly Func<T> _defaultValueFactory = (Func<T>)DefaultT;
private Func<T> DefaultValueFactory
{
    get
    {
        return _defaultValueFactory;
    }
}

private static T DefaultT()
{
    return default(T);
}

Также включите конструктор для вашего типа, который принимает Func<T> defaultValueFactory.

Везде, где вы обычно используете new T(), используйте вместо этого DefaultValueFactory().

person Sam Harwell    schedule 16.03.2010

Все пустые строки одинаковы. Почему вы хотите его создать? Вы можете использовать String.Empty или просто "".

person codymanix    schedule 16.03.2010

Из http://msdn.microsoft.com/en-gb/library/system.string.aspx

Объект String называется неизменяемым (только для чтения), потому что его значение нельзя изменить после того, как он был создан. Методы, которые кажутся изменяющими объект String, фактически возвращают новый объект String, содержащий изменение. Если необходимо изменить фактическое содержимое строкового объекта, используйте класс System.Text.StringBuilder.

Так что конструктор без параметров не имеет смысла.

person Morfildur    schedule 16.03.2010
comment
Неизменяемость вовсе не означает, что у объекта не должно быть конструкторов без параметров. Показательный пример: каждый тип значения является неизменяемым и имеет конструктор без параметров (т.е. DateTime d = new DateTime()), и все многочисленные реализации неизменяемых структур данных будут представлять пустой узел с конструктором без параметров. - person Juliet; 16.03.2010
comment
Да, типы значений должны быть неизменными и иметь конструктор без параметров. - person JoeG; 16.03.2010
comment
Типы значений не неизменяемы. Если вы создаете структуру, вы можете изменить ее свойства (struct Foo {public int bar;} Foo foo = new Foo (); foo.bar = 1; foo.bar = 2; ...). Это невозможно для струнных. Свойства, возвращающие значение, являются неизменяемыми, однако изменение возвращаемого значения не влияет на исходное значение, поскольку из свойства возвращается только копия. - person Morfildur; 16.03.2010
comment
@dbemerlin: вы можете создать изменяемый тип значения, но почти все согласны с тем, что это зло, и использование их в любом серьезном производственном коде является основанием для порки. - person Juliet; 16.03.2010

Что бы

String s = new String();

дать тебе

String s = String.Empty;

or

String s = null;
person Robin Day    schedule 16.03.2010
comment
Почему при обновлении экземпляра объекта вы можете получить значение NULL? - person Juliet; 16.03.2010
comment
@Juliet - если объект реф. type и принадлежит к классу, для которого по умолчанию установлено значение null. - person JonH; 16.03.2010
comment
@ Джульетта, я понимаю, что не понимаю. Я имел в виду, что если вам нечего передать конструктору, вы, вероятно, захотите, чтобы он был либо пустым, либо нулевым. - person Robin Day; 16.03.2010

Int32 - это структура. У структур ВСЕГДА есть конструктор без параметров. Это не имеет ничего общего с его полезностью - даже если Microsoft решит, что конструктор без параметров Int32 полностью и совершенно бесполезен, они абсолютно ничего не могут с этим поделать. И если бы они захотели изменить поведение структур сейчас, это было бы огромным критическим изменением. Превращение Int32 в класс также вообще невозможно, поскольку существует причина, по которой .net поддерживает как тип значения, так и тип ссылки (я слышал от парней из Java, что тот факт, что Integer является классом, вызывает некоторые проблемы, но я не могу комментировать на то действительно).

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

person Michael Stum    schedule 16.03.2010