代码之家  ›  专栏  ›  技术社区  ›  empi

字符串-为什么它不实现无参数构造函数?

  •  25
  • empi  · 技术社区  · 16 年前

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

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

    更新

    为什么你认为new string()是无用的,例如new int()不是无用的。我真的看不出有什么不同。我真的希望能够对string使用一个新的()约束,因为我期望的是string.empty。

    更新2

    我知道int是一个值类型,所有值类型都有默认的无参数构造函数。 我指的是@john saunders answer(和类似的答案),它指出说new string()基本上没有任何用处。所以如果new string()不意味着任何有用的东西,那么new int()有什么用处呢?我也理解值类型有默认的无参数构造函数是很重要的。更重要的是,我认为字符串有一个默认的无参数构造函数是非常好的。缺少无参数构造函数对我来说意味着如果没有初始化对象的参数,对象就不可能存在。在我看来字符串不需要任何参数,因为它可能是空的。我问这个问题是因为我想知道字符串没有无参数构造函数的重要原因,或者它只是设计或其他什么。

    更新3

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

    初始化新创建的字符串 对象,以便它表示空的 字符序列。注意使用 此构造函数是不必要的,因为 字符串是不可变的。

    更新4

    好的,最后更新;)我必须同意约翰桑德斯。我使用了带有new()约束的代码,它在自定义类中运行得很好。然后,我的同事需要更改ints的代码,这没问题。然后把它改成字符串,我们遇到了一个问题。我现在想起来,我想我们使用的代码需要修改,以反映我们需要的是标量类型,而不是具有无参数构造函数的类。尽管如此,我仍然认为你能写new int()和不能写new string()有点不相干(是的,我理解int是一种值类型;)。谢谢你的回答!

    提前谢谢你的帮助。

    11 回复  |  直到 9 年前
        1
  •  25
  •   John Saunders    16 年前

    如果你能利用

    string s = new string();
    

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


    一些读者认为我是在说一个无参数的字符串构造函数将是无用的。我不是这个意思。我的意思是,从这样一个构造函数创建的空字符串在代码的其余部分将是无用的。

    记住手术医生说他想做什么:

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

    我不知道你能用什么 t 在它建成之后。

        2
  •  11
  •   Juliet    16 年前

    为什么你认为新的string()是 无用,例如new int()不是 无用的。

    让我们回到基础部分,讨论引用和值类型。

    当您创建一个int、long、point或其他类型时,值类型被分配到堆栈上——因此int在堆栈上保留4个字节,long是8个字节,依此类推。为值类型保留的堆栈空间为0'd out——不可能引用堆栈上的值类型地址并获取空值。因此,值类型上的默认构造函数可能更多地是由于堆栈分配的异常而不是有用性(也许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;
    }
    

    我们不能使用新的约束,因为有些表单有非默认的构造函数,所以上面的策略最终很好地解决了问题,并且避免了创建大量工厂类的麻烦。

        3
  •  4
  •   Dan Tao    16 年前

    我猜你想定义一个类,它是这样的:

    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() 约束)。

        4
  •  3
  •   Glory Raj    14 年前

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

    你可以这样做:

      String s = "hello world";
    

    一旦有了字符串,就不能更改该特定字符串(strimgs是不可变的)。如果要声明空字符串,请执行以下操作:

    string s = string.empty
    
        5
  •  1
  •   Mark Brackett    16 年前

    “[为什么]system.string…不包含[a]无参数构造函数?”

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

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

    不同的是 int 是一个 ValueType . 小精灵 S是 required 有一个默认构造函数,并将类型初始化为默认值。 一串 是引用类型-它已具有默认值- null (与所有其他引用类型相同)。

    当然,你不同意-你有一个特定的场景,你觉得用默认的ctor来实现它是最好的 一串 . 如果不知道你的具体情况,很难明智地评论。但是,我猜你在处理 primitive obsession . 如果 string.Empty 对于您的特定数据来说是有意义的,那么您可能应该有一个类围绕它。它的默认构造函数可以初始化 空字符串 .

    E:为什么原始的痴迷?

    我要写关于orms或其他映射程序的文章(我能想到的几个地方 where T:new() 是有用的)以及如何,而不是试图映射 一串 s,您应该使用自定义业务对象。但是,在做了一些示例代码之后-我被卡住了。我想不出任何有意义的场景 new string() == string.Empty .

        6
  •  1
  •   Sam Harwell    16 年前

    具体解决如何避免 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
  •  1
  •   JoeG    16 年前

    这是泛型代码中常见的问题。C++用模板特化解决了这个问题,但是C不支持。

    不过,用C语言是不可能干净利落的——你最好用 class 相反,使用基于反射的hack来默认构造 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);
    
        8
  •  0
  •   codymanix    16 年前

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

        9
  •  0
  •   Sam Harwell    16 年前

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

    String 对象被称为不可变(只读),因为它的值 创建后无法修改。出现的方法 修改一个 对象实际上返回一个新的 对象 包含修改的。如果需要修改 类字符串对象的实际内容,使用 System.Text.StringBuilder 班级。

    所以一个无参数的构造函数是没有意义的。

        10
  •  -1
  •   Robin Day    16 年前

    什么会

    String s = new String();
    

    把你交出来

    String s = String.Empty;
    

    String s = null;
    
        11
  •  -1
  •   Michael Stum    16 年前

    int32是一个结构。结构总是有一个无参数的构造函数。这与它的实用性无关——即使微软认为Int32的无参数构造函数完全没有用处,他们也绝对无能为力。如果他们现在想改变结构的行为,那将是一个巨大的突破性的改变。将T32转换成一个类也是不可行的,因为.NET支持值和引用类型的原因(我从Java家伙那里听到,整型是一个类的事实引起了一些问题,但我不能对此进行评论)。

    另一方面,string是一个类。对于类,您可以控制is是否具有无参数构造函数,因此在这里可以看到它是无用的并忽略它。

    推荐文章