System.String - 为什么不实现无参数构造函数?

时间:2010-03-16 14:02:25

标签: .net string constraints

我想问一下System.String不包含无参数构造函数背后的想法是什么。

因此,当存在new()约束时,不能使用此类型。

更新

为什么你认为新的字符串()是无用的,例如new int()没用。我真的看不出差异。我真的希望能够对字符串使用new()约束,因为我期待的是String.Empty。

UPDATE2

我知道int是一个值类型,所有值类型都有默认的无参数构造函数。 我指的是@John Saunders的回答(和类似的答案),说明新的String()基本上没有做任何有用的事情。那么,如果新的String()并不意味着什么对新的int()有用呢?我也明白,值类型具有默认的无参数构造函数非常重要。更重要的是我认为字符串有一个默认的无参数构造函数真的很好。缺乏无参数构造函数对我来说意味着如果没有将初始化它的参数,对象就不会存在。在我看来,字符串不需要任何参数,因为它可能是空的。我问了这个问题,因为我想知道字符串是否有一些重要原因没有无参数构造函数,或者它只是设计还是其他。

UPDATE3

我想这将是最后一次更新,因为它看起来更像博客文章而不是问题。在java中,字符串也是不可变的,新的String()是完全合法的。但是,无参数构造函数的文档说:

  

初始化新创建的String   对象,以便它代表一个空的   字符序列。注意使用   这个构造函数是不必要的   字符串是不可变的。

UPDATE4

好的,上次更新;)我必须同意John Saunders。我使用的是具有new()约束的代码,它适用于自定义类。然后,我的同事需要更改int的代码,这没关系。然后将其更改为字符串,我们遇到了问题。当我想到它时,我想我们正在使用的代码需要改变以反映我们需要标量类型而不是具有无参数构造函数的类。所有这一切,我仍然认为你可以编写新的int()并且你无法编写新的string()(是的,我知道int是一种值类型;))有点不一致。谢谢你的所有答案!

提前感谢您的帮助。

11 个答案:

答案 0 :(得分:25)

如果你可以使用

string s = new string();

你会用它做什么?字符串是不可变的。


有些读者认为我说无参数字符串构造函数是无用的。那不是我要表达的意思。我的意思是从这样的构造函数创建的空字符串在其余代码中将是无用的。

请记住OP说他想做的事情:

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

// Call:
SomeMethod<string>();

我看不出t在构建之后你能做些什么。

答案 1 :(得分:11)

  

为什么你认为新的字符串()是   没用,例如new int()不是   无用的。

让我们回到基础,讨论参考和价值类型。

当你创建一个int,long,point或者其他什么时,值类型被分配在堆栈上 - 所以int在堆栈上保留4个字节,long是8个字节,依此类推。为值类型保留的堆栈空间为零 - 它不可能引用堆栈上的值类型地址并获得null。因此,值类型的默认构造函数可能更多地来自堆栈分配中的怪癖而不是有用性(也许MS的某个人可以提供更多的洞察力)。

在任何情况下,我认为System.String缺少默认构造函数,因为糟糕的API设计

不可变对象 - 特别是可枚举 - 几乎总是暴露默认构造函数来实例化一个空集合。

我认为微软有良好的意图:当你需要一个空字符串时, 更好地使用""String.Empty,因为它们是实习的,而且不是那么有用每次新增new String() - 但它以微软的方式“将开发人员从自己身上拯救出来”来看待我。

(感谢微软,我感谢你的家长式手持,但我完全有能力照顾自己。如果我不想创建同一个对象的多个实例,我会像我一样缓存它使用默认构造函数为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;
}

我们无法使用新约束,因为某些形式具有非默认构造函数,因此上述策略最终确实很好并且为我们节省了创建大量工厂类的麻烦。

答案 2 :(得分:4)

我猜你想要能够定义一个类似这样的类:

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()约束的东西)都可以。< / p>

答案 3 :(得分:3)

什么是没有实际字符串的字符串?

你可以这样做:

  String s = "hello world";

一旦你有了字符串,你就无法改变那个特定的字符串(strimgs是不可变的)。如果您要声明一个空字符串,那么只需执行:

string s = string.empty

答案 4 :(得分:1)

这是通用代码中的常见问题。 C ++使用模板特化来解决它,但C#不支持它。

虽然不能在C#中干净利落地完成 - 但最好的办法是使用class约束,然后使用基于反射的黑客来默认构建T或使用String.Empty如果TSystem.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);

答案 5 :(得分:1)

  

“[为什么不] System.String ...包含[a]无参数构造函数?”

因为这是它的设计方式。从这里的各种答案和意见中可以看出,对无参数构造函数的需求或实现甚至没有明确的共识。显然,技术上可以有一个初始化为String.Empty的默认构造函数 - 所以要么BCL团队没有想到它(不太可能),要么觉得这不是一个有价值的场景(可能)。 FWIW,我同意 - 我不明白为什么string上的默认ctor很重要。

  

“为什么你认为新的string()是无用的,例如new int()没用。我真的看不出差别。”

区别在于intValueTypeValueType required 具有默认构造函数,并将类型初始化为默认值。 string是一种引用类型 - 它已经有一个默认值 - null(与所有其他引用类型相同)。

当然,你不同意 - 你有一个特定的场景,你觉得最好用string上的默认ctor实现。在不了解您的场景细节的情况下,很难对其进行智能评论。但是,我猜你正在处理primitive obsession。如果string.Empty对您的特定数据有意义,那么您应该有一个类围绕它。它的默认构造函数可以初始化string.Empty

E:为什么原始的痴迷?

我打算写关于ORM或其他地图制作者(我能想到的where T:new()很有用的几个地方)以及如何使用string来映射new string() == string.Empty,你应该使用自定义业务对象。但是,在做了一些示例代码之后 - 我很困惑。我无法想到任何使用{{1}}有意义的场景。

答案 6 :(得分:1)

具体说明在使用泛型类型时如何避免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()

答案 7 :(得分:0)

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

  

String对象称为immutable(只读),因为它的值       一旦创建,就无法修改。出现的方法       修改String对象实际上返回一个新的String对象       包含修改。如果有必要修改       类似字符串的对象的实际内容,使用System.Text.StringBuilder       类。

所以无参数构造函数没有意义。

答案 8 :(得分:0)

所有空字符串都是相同的。你为什么要创建一个?您可以使用String.Empty或简单地使用“”。

答案 9 :(得分:-1)

会是什么

String s = new String();

告诉你

String s = String.Empty;

String s = null;

答案 10 :(得分:-1)

Int32是一个结构。结构总是有一个无参数构造函数。它与它的用处无关 - 即使微软认为Int32的无参数构造函数是完全无用的,它们也无能为力。如果他们现在想改变结构的行为,那将是一个巨大的突破性变化。将Int32转换为类也完全不可行,因为有.net支持Value和Reference类型的原因(我从Java人那里听说Integer是一个类的事实会导致一些问题,但我无法评论在那真的)。

另一方面,字符串是一个类。对于类,您可以控制是否具有无参数构造函数,因此可以看到它没用,并省略它。