【问题标题】:Why should each element in array be allocated again in c#为什么要在c#中重新分配数组中的每个元素
【发布时间】:2013-05-07 14:15:23
【问题描述】:

以下是我写的代码 Calc[] calculators = new Calc[10]; calculators[0].AddToSum(10);(写了对应的类和方法)。 但是我得到“对象引用未设置为对象的实例”异常。然后通过一些研究,我通过执行以下操作删除了异常。

for (int i = 0; i < 10; i++)
        {
            calculators[i] = new Calc();
        }  

有人可以解释为什么我们需要重新分配内存,不像在 c/c++ 中那样。 这就是我在 C++ 中的做法:

Calculator *calc=new Calculator[10]//I know I need to check for std::bad_alloc exception
calculators[0].AddToSum(10); 
delete[] calc;

【问题讨论】:

    标签: c# c++ .net memory-management console-application


    【解决方案1】:

    在 C# 中,有引用类型,也有值类型。类是引用类型。当你创建一个引用类型的变量时,你是在创建一个引用,而不是一个对象。引用的默认状态为空。如果你想让它引用一个对象,你必须用new显式初始化它,或者从另一个初始化的引用中赋值。

    C++ 没有这种区别。每种类型都是值类型(尽管您也可以创建对任何类型的引用)。当你创建一个值类型的变量时,你就是在创建一个对象。

    【讨论】:

      【解决方案2】:

      在 new Calc[10] 中,您正在分配和调整数组大小。在 new Calc() 中,您正在创建实际的 Calc 对象

      【讨论】:

        【解决方案3】:

        但是你会得到与此语句相同的错误

        Calc calc; 
        calc.AddToSum(10);
        

        在您分配值之前,对象为空。

        Calc[] 计算器 = new Calc[10];不分配。

        根据 Benjamin (+1) 的回答,如果 Calc 是引用类型,它就可以工作。
        你能把 Calc 做成一个结构吗?

        【讨论】:

        • 使用 Calc 计算; calc.AddToSum(10);无论 Calc 的类型是结构/类,代码都无法编译;结果错误 - 使用未分配的局部变量。但我明白了这一点,在 c# 中,用户定义类型的数组是引用类型,它们就像指向 null 的束指针。所以必须用 new 分配单个元素。
        • 错误。用户定义类型不限于引用类型。如果 Calc 是一个结构,那么您不需要初始化数组中的元素。
        • @Balm 感谢您的纠正,我尝试将 Calc 设为 struct 并做了 Calc[] calculators = new Calc[10];计算器[0].AddToSum(10);它似乎不像类那样抛出异常。所以结构是值类型,类是引用类型。对吗?
        【解决方案4】:

        我不认为你再次分配内存,但你仍然需要为calculators[0] 实例化一些值。

        在您的第一个代码段中,您正尝试在 Null 的值上调用 .AddToSum

        Ps:您可以改为执行以下操作,从头开始初始化每个 Calc

        Calc[] calculators = new Calc[10]{  
                                           new Calc(), 
                                           new Calc(), 
                                           ..., 
                                           // Repeat 10 times to match array length
                                          };
        

        更新:回应以下cmets;好的,那就试试这个:

        calc[] calculators = Enumerable.Repeat(new Calc(), 127).ToArray<Calc>();
        

        【讨论】:

        • 那将是可怕的代码维护。如果你想要 87 个 Calc 怎么办
        • 是的,很明显。只是指出它是一种可能性,作为对比,OP 强调了Null 问题。显然有更好的方法来填充包含许多项目的数组(如果您只需要两个或三个项目,这可能会很好。要求可能会有所不同)。
        • @bland 我明白你的意思,但请注意,这会创建一个列表,而不是一个数组(当然,这可以以与我的更新相同的方式修复..)。
        【解决方案5】:

        当您在 C++ 中创建对象数组时,您会为每个对象的所有字段分配内存。因此,如果您的对象有两个整数字段,并且您创建了一个大小为 2 的数组,则会分配足够的内存来保存四个整数。

        另一方面,在 c# 中,当您创建要创建的对象数组和引用数组(指向对象的指针)时。因此,除非您为每个引用分配内存(通过使用 new),否则您无法存储实例。

        在 c++ 中同样的事情是创建一个指针数组,然后您必须实例化数组的每个元素。

        【讨论】:

        • 我同意前两段;但第三段对我来说似乎是错误的。当你执行 Calc *calc = new Calc[10]; new 负责实例化,即分配内存并为所有 10 个对象调用构造函数。但是在 c# 中,正如您所说,它只是创建了一堆指针,这些指针应该单独分配内存以及构造。
        • @ZoomIn:我的意思是这样的:Calculator** calc = new Calculator*[10];
        【解决方案6】:

        您的 C++ 代码也是错误的。 在 C++ 中,您已经为 10 个 Calculator 对象分配了一个包含空间的数组。 当您执行操作时,它会从该(未初始化的)内存中读取,获取一个值并将其添加到其中,然后将其写回。 但是你有一个未初始化的对象开始。

        它可能在 C++ 中有效,因为您有一个不需要调用构造函数的对象(计算器)。如果它有任何需要调用构造函数的初始化,它将不起作用。如果您要使用调试器并在 Calculator 构造函数中放置一个断点,您会发现它从未被调用过。

        无论如何,直接回答这个问题,这就是 C# 的工作方式。分配数组会为数组创建空间,但数组中的所有对象(假设对象类型)在分配之前都是空的。

        这样想:我创建了一个数组来保存 X 类的 10 个对象。但是 X 有一个接受字符串的构造函数,我想为每个对象使用不同的字符串来调用它。如果不显式创建这 10 个对象中的每一个并将正确的字符串传递给每个构造函数,如何做到这一点?

        【讨论】:

        • 您关于 C++ 的信息不正确。在 C++ 变量声明中 is 对象构造。当你声明一个数组或动态分配一个数组时,该数组中所有对象的构造函数都会被调用。
        • 我很抱歉......你是对的。我在 C#/Java 世界中待了这么久,以至于我忘记了这种区别。所以对原始问题的简单回答是,这只是 C++ 和 Java/C# 处理数组的方式不同。
        猜你喜欢
        • 2021-03-15
        • 1970-01-01
        • 1970-01-01
        • 2015-05-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-06
        • 1970-01-01
        相关资源
        最近更新 更多