System.String - почему он не реализует конструктор без параметров? - PullRequest
25 голосов
/ 16 марта 2010

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

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

UPDATE

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

UPDATE2

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

Update3

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

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

ОБНОВЛЕНИЕ4

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

Заранее спасибо за помощь.

Ответы [ 11 ]

25 голосов
/ 16 марта 2010

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

string s = new string();

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


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

Имейте в виду, что ОП сказал, что он хотел сделать:

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

// Call:
SomeMethod<string>();

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

11 голосов
/ 16 марта 2010

Почему вы думаете, что новая строка () бесполезный и, например, новый 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;
}

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

4 голосов
/ 16 марта 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()).

3 голосов
/ 16 марта 2010

Что такое строка без фактической строки?

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

  String s = "hello world";

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

string s = string.empty
1 голос
/ 16 марта 2010

Конкретно рассматривается, как избежать проблемы ограничения 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().

1 голос
/ 16 марта 2010

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

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

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

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

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

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

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

1 голос
/ 16 марта 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);
0 голосов
/ 16 марта 2010

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

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

0 голосов
/ 16 марта 2010

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

0 голосов
/ 16 марта 2010

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

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

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

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