【问题标题】:Passing array by value in c#在c#中按值传递数组
【发布时间】:2020-05-23 22:29:15
【问题描述】:

据我了解,传入c# 的默认类型或参数是按值传递的。因此不需要声明。但是,当我尝试运行以下代码时,Main 中的A 矩阵正在被Decomposition 类的Factorize() 方法中对dMatrixU 所做的操作修改。我确定问题出在Decomposition 的构造函数中,当我将A 分配给dMatrixU 时,分配的是A 的引用而不是值。因此,关于如何避免这种情况的问题,我发现的只是如何通过引用传递参数。同样,据我所知,按值传递参数不需要修饰符。我哪里错了?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using LinearEquations;

namespace Rextester
{
    public class Program
    {
        public static void Main(string[] args)
        {
            double[,] A = new double[,]
              { { 1, 1, 1  }  ,
                { 4, 3, -1 }  ,
                { 3, 5, 3  } };
            double[] B = new double[] {1,6,4};
            Decomposition lu = new Decomposition(A,B);
            lu.Factorize();
            PrintMatrix(A,"A:");
            PrintVector(B,"B:");
            PrintMatrix(lu.L,"L:");
            PrintMatrix(lu.U,"U:");
            PrintVector(lu.D,"D:");
        }
        public static void PrintMatrix(double[,] M, String Title = "Matrix: ")
        {
            Console.WriteLine(Title);
            for(int i = 0; i<M.GetLength(0); i++)
            {
                for(int j = 0; j<M.GetLength(1);j++)
                {
                    Console.Write(M[i,j]+"\t");
                }
                Console.Write("\n");
            }
            Console.Write("\n");
        }
        public static void PrintVector(double[] V, String Title = "Vector: ",bool AsRow = true)
        {
            String str = (AsRow)? "\t" : "\n";
            Console.WriteLine(Title);
            for(int i = 0; i<V.GetLength(0); i++)
            {
                Console.Write(V[i]+str);
            }
            Console.WriteLine("\n");
        }
    }
}

namespace LinearEquations
{
    public class Decomposition
    {
        // Fields
        private double[,] dMatrixA;  // Parameter in A*X=B
        private double[] dVectorB;  // Parameter in A*X=B
        private double[] dVectorX;  // Result wanted in A*X=B
        private double[,] dMatrixU; // A splits into L and U
        private double[,] dMatrixL; // L is used to calculate D in L*D=B
        private double [] dVectorD; // D is used to calculate X in U*X=D

        // Properties
        public double[,] A
        {
            get { return dMatrixA; }
            set { dMatrixA = value; }
        }
        public double[] B
        {
            get { return dVectorB; }
            set { dVectorB = value; }
        }
        public double[] X
        {
            get { return dVectorX; }
            set { dVectorX = value; }
        }
        public double[,] L
        {
            get { return dMatrixL; }
            set { dMatrixL = value; }
        }
        public double[,] U
        {
            get { return dMatrixU; }
            set { dMatrixU = value; }
        }
        public double[] D
        {
            get { return dVectorD; }
            set { dVectorD = value; }
        }

        // Constructor
        public Decomposition(double[,] A, double[] B)
        {
            dMatrixA = A;
            dVectorB = B;
            dVectorX = new double[B.Length];
            dMatrixU = A;
            dMatrixL = new double[A.GetLength(0),A.GetLength(1)];
            dVectorD = new double[B.Length];
        }

        // Split A into L and U
        public void Factorize()
        {
            // Iterate per each row
            for(int i = 0; i<dMatrixU.GetLength(0); i++)
            {
                // For all the rows make element i equals 0
                for(int j = i+1; j<dMatrixU.GetLength(0);j++)
                {
                    // Factor that assures substraction makes 0
                    dMatrixL[1,1] = dMatrixU[j,i] / dMatrixU[i,i];

                    // Iterate per each column
                    for(int k = 0; k<dMatrixU.GetLength(1);k++)
                    {
                        dMatrixU[j,k] = dMatrixU[j,k] - dMatrixU[i,k]*dMatrixL[1,1];
                    }
                }
            }
        }

    }
}

【问题讨论】:

  • 引用传递和值传递不同于引用类型和值类型。
  • 使用Array.Copy 生成数组的副本。数组通过引用传递。
  • 对数组的引用是传值的。
  • 实际上数组地址的副本被传递给方法。但是原始变量和方法参数(复制变量)都指向同一个内存空间,因为数组是引用类型。因此,当你改变函数中的元素时,实际上是在改变内存中的原始值。

标签: c# arrays pass-by-value


【解决方案1】:

据我了解,在 c# 中传递的默认类型或参数是按值传递的。

不幸的是,它有点复杂,也有一些例外:

引用类型,如 Decomposition,您可以通过复制引用来提交。不幸的是,这意味着两者仍然引用内存中的相同实例。因此,尽管有复制操作,但它是按引用调用的。

对于像Intdouble 这样的值类型及其别名,通常会创建一个副本。我不知道它没有的任何情况,但我以前在那些事情上错了。所以它们是按值调用的。

最后String 和其他一些引用类型在设计上是不可变的。这样做的好处是它们在这个领域的行为有点像值类型。您提交一个参考,但实例本身不能更改。该代码只能在内存中创建一个具有不同值的新实例。因此,尽管移交了文字引用,但它有点像按值调用。

您的具体情况

数组是非常明确的引用类型。将它们放入没有副作用的函数中,需要适当的克隆。如果是引用类型数组,则克隆一定是深的。

在您的情况下,您有值类型的数组。如果您想避免按引用调用的副作用,则必须克隆这些数组。然而,由于 double 是一种值类型,this cloning can be shallow。无需深度克隆。

与 Java 不同,没有专用的 Clone() 方法。我不确定为什么。但是,您通常可以使用一个 Collection 通过构造函数来初始化另一个 Collection。或者他们甚至有像Array.Copy() 这样的功能,正如蝙蝠侠指出的那样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-22
    • 1970-01-01
    • 2014-09-08
    • 1970-01-01
    • 2020-12-28
    • 2015-08-03
    • 1970-01-01
    相关资源
    最近更新 更多