开发者

System.String - why doesn't it implement parameterless constructor?

开发者 https://www.devze.com 2022-12-23 05:51 出处:网络
I wanted to ask what is the idea behind the fact that System.String doesn\'t contain parameterless constructor.

I wanted to ask what is the idea behind the fact that System.String doesn't contain parameterless constructor.

Because of this you cannot use this type when there is new() constraint.

UPDATE

Why do you think that new string() is useless and e.g. new int() is not useless. I really cannot see the difference. I really would like to be able to use a new() constraint on string, since what I'm expecting is String.Empty.

UPDATE2

I know that int is a value type and all value types have default parameterless constructor. I was referring to @John Saunders answer (and similar answers) that were stating that saying new String() basically doesn't do anything useful. So if new String() doesn't mean anything useful what is so useful about new int()? I also understand that it is important for value types to have default parameterless constructors. What is more I think that it would be really nice for string to have a default parameterless constructor. Lack of a parameterless constructor means for me that an object simply cannot exist without a parameter that will initialize it. In my opinion string doesn't need any parameter because it could be empty. I asked this question because I wanted to know if there is some important reason for string not having parameterless constructor or it's just design or sth else.

UPDATE3

I guess it will be the last update since it looks more like a blog post than a question. In java strings are also immutable and new String() is perfectly legal. However, documentation for parameterless constructor says:

Initializes a newly created String object so that it represents an empty character sequence. Note that use of this constructor is开发者_StackOverflow社区 unnecessary since Strings are immutable.

UPDATE4

Ok, last update ;) I have to agree with John Saunders. I was using code that had new() constraint and it worked nicely for custom classes. Then, my colleague need to change the code for ints and it was ok. Then change it for string and we had a problem. When I now think about it I guess the code we are using needs to be changed to reflect that we need scalar types not classes that have parameterless constructor. All that said, I still think it's a bit inconsisent that you can write new int() and you are unable to write new string() (yes, I understand that int is a value type ;)). Thanks for all your answers!

Thanks in advance for help.


If you could use

string s = new string();

What would you do with it? Strings are immutable.


Some readers have thought I was saying that a parameterless string constructor would be useless. That's not what I meant. I meant that the empty string created from such a constructor would be useless in the rest of the code.

Keep in mind what the OP said he wanted to do:

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

// Call:
SomeMethod<string>();

I don't see what you could do with t after it was constructed.


Why do you think that new string() is useless and e.g. new int() is not useless.

Let's go back to basics and talk about reference and value types.

When you create an int, long, point, or whatever, the value type is allocated on the stack -- so an int reserves 4 bytes on the stack, long is 8 bytes, and so on. The stack space reserved for the value type is zero'd out -- its not possible ever to refer to value types address on the stack and get a null. So default constructors on value types probably result more from quirks in stack allocation than usefulness (maybe someone at MS could give more insight).

In any case, I think System.String lacks a default constructor because of lousy API design.

Immutable objects -- enumerables in particular -- almost always expose a default constructor to instantiate an empty collection.

I think Microsoft had good intentions: it is better to use "" or String.Empty whenever you need an empty string because they're interned, and not so useful to new up a new String() everytime -- but it comes across to me as Microsoft's way of "saving the developers from themselves".

(Thanks Microsoft, I appreciate your paternalistic hand-holding, but I'm perfectly capable of taking care of myself. If I don't want to create multiple instances of the same object, I'll cache it just like I do for the bagillion other types with a default constructor.)

For now, you need to treat objects without default constructors as a special case from everything else. Rewrite

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

As two methods:

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

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

Clients to your code should decide which function to call for themselves.


Now someone looking at the code above might ask why you'd pass a constructor instead of an actual object into the method, i.e.

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

There might be reasons, for example if the constructor method isn't guaranteed to be called. I worked complex winform app where clicking buttons or menu items would pop up a form -- or, if the form was already open, we'd focus it instead. So we had something like:

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;
}

We couldn't use the new constraint since some forms had non-default constructors, so the strategy above ultimately worked out really well and saved us the trouble of creating a ton of factory classes.


I'm guessing you want to be able to define a class that's something like this:

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
}

Am I right? If you want this to be more flexible, to allow for types that may not have a default parameterless constructor, you can always do this:

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());
    }
}

Then you could create a SpecializedCollection<string> like so:

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

Of course, if you wanted to offer a default constructor that would attempt to construct objects using a parameterless constructor for T, you could always overload the constructor:

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

The above wouldn't work for string, of course, but it would for any class with a parameterless constructor (i.e., anything that would've worked with the old where T : new() constraint).


What is a string without an actual string?

You can do this:

  String s = "hello world";

And once you have the string you cannot alter that specific string (strimgs are immutable). If you are looking to declare an empty string then just do:

string s = string.empty


It's a common problem in generic code. C++ solves it with template specialization, but C# doesn't support that.

It can't be done cleanly in C# though - your best bet is to use the class constraint instead, then use reflection based hack to default construct a T or use String.Empty if T is System.String. You'll lose compiler checking for using other classes which aren't default constructable though.

Here's another option:

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);

And another:

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);


"[Why doesn't] System.String...contain [a] parameterless constructor?"

Because that's the way it was designed. As you can see from the variety of answers and opinions here, there's not even a clear consensus on the need or implementation of a parameterless constructor. Obviously, it's technically possible to have a default constructor that initializes to String.Empty - so either the BCL team didn't think of it (unlikely), or didn't feel it was a worthwhile scenario (likely). FWIW, I agree - I don't see a reason why a default ctor on string is important.

"Why do you think that new string() is useless and e.g. new int() is not useless. I really cannot see the difference."

The difference is that int is a ValueType. ValueTypes are required to have a default constructor, and it initializes the type to a default value. string is a reference type - it already has a default value - null (the same as all other reference types).

Of course, you disagree - you have a specific scenario that you feel is best implemented with a default ctor on string. Without knowing specifics about your scenario, it's hard to comment on it intelligently. But, my guess is that you're dealing with primitive obsession. If string.Empty makes sense for your particular data, then you should probably have a class around it. It's default constructor can initialize a string.Empty.

E: Why primitive obsession?

I was going to write about ORMs or other mappers (the few places that I can think of that where T:new() is useful) and how, instead of trying to map strings, you should be using a custom business object. But, after doing some example code - I'm pretty stuck. I can't think of any scenario that makes sense with a new string() == string.Empty.


Specifically addressing how to avoid the new() constraint problem when you're working with generic types:

Include the following field, property, and method:

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

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

Also include a constructor for your type that takes a Func<T> defaultValueFactory.

Wherever you normally use new T(), use DefaultValueFactory() instead.


All empty Strings are the same. Why do you want to create one? You can use String.Empty or simply "".


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

A String object is called immutable (read-only) because its value cannot be modified once it has been created. Methods that appear to modify a String object actually return a new String object that contains the modification. If it is necessary to modify the actual contents of a string-like object, use the System.Text.StringBuilder class.

So a parameterless constructor wouldn't make sense.


What would

String s = new String();

give you over

String s = String.Empty;

or

String s = null;


Int32 is a struct. Structs ALWAYS have a parameterless constructor. It has nothing to do with it's usefulness - even if Microsoft decided that Int32's parameterless constructor is completely and utterly useless, there is absolutely nothing they could do about it. And if they wanted to change the behavior of structs now, it would be a huge breaking change. Turning Int32 into a class is also not feasible at all as there is a reason why .net supports both Value and Reference type (I hear from the Java guys that the fact that Integer is a class causes some problems, but I can't comment on that really).

String on the other hand is a class. For classes, you can control whether or not is has a parameterless constructor, so here it was possible to see that it's useless and omit it.

0

精彩评论

暂无评论...
验证码 换一张
取 消