按价值传递?
Passing by value?
我在传递 C# 函数参数时遇到问题。
我想知道如何使 C# 函数按值接受参数(以复制原始对象)。
我认为这是 C# 处理这些事情的默认方式,但在以下代码中:
using System;
using System.Collections.Generic;
using System.Linq;
class MaximumElement
{
static void Main(string[] args)
{
Stack<int> numbers = new Stack<int>();
int n = int.Parse(Console.ReadLine());
for (int i = 0; i < n; i++)
{
string input = Console.ReadLine();
switch (input)
{
case "2": numbers.Pop(); break;
case "3": Console.WriteLine(maxElement(numbers)); break;
default:
string[] argz = input.Split(' ');
numbers.Push(int.Parse(argz[1]));
break;
}
}
}
public static int maxElement(Stack<int> stack)
{
int max = stack.Peek();
for (int i = 0; i < stack.Count; i++)
{
if (max >= stack.Peek())
{
stack.Pop();
}
else if (max < stack.Peek())
{
max = stack.Pop();
}
}
return max;
}
}
我的maxElement()
函数实际上改变了我传递给它的原始堆栈,唯一绕过它的方法是手动复制我的堆栈传递给函数内部的函数。
感谢您提前回复 :)
您需要复制 Stack,如果浅拷贝有效,您可以使用 Clone() 方法。
有点复杂。来自 MSDN (https://msdn.microsoft.com/en-us/library/s6938f28.aspx):
A variable of a reference type does not contain its data directly; it contains a reference to its data. When you pass a reference-type parameter by value, it is possible to change the data pointed to by the reference, such as the value of a class member. However, you cannot change the value of the reference itself; that is, you cannot use the same reference to allocate memory for a new class and have it persist outside the block. To do that, pass the parameter using the ref or out keyword. For simplicity, the following examples use ref.
这是他们提供的代码示例:
static void Change(int[] pArray)
{
pArray[0] = 888; // This change affects the original element.
pArray = new int[5] {-3, -1, -2, -3, -4}; // This change is local.
现在,如果您在参数中使用 ref
关键字
static void Change(ref int[] pArray)
{
pArray[0] = 888; // This change affects the original element.
pArray = new int[5] {-3, -1, -2, -3, -4}; // This change also affects the original
所以,考虑到这些,您可以...
public static int maxElement(Stack<int> stack)
{
stack = new Stack<int>(stack); // Now changes will be local
int max = stack.Peek();
for (int i = 0; i < stack.Count; i++)
{
if (max >= stack.Peek())
{
stack.Pop();
}
else if (max < stack.Peek())
{
max = stack.Pop();
}
}
return max;
}
不要将 value 或 reference 的传递参数与 value 类型 和 引用类型。这是初学者常犯的错误,您需要清楚地了解这两者虽然在某种程度上相关,但却是语言的完全不同的特性。
我可能不会使用精确的术语,因为英语不是我的语言,但我希望我能理解这个想法:
- 值类型:变量就是值本身。当您编写以下内容时:
int i = 1;
变量 i
保存值 1
.
- 引用类型:变量是指向内存中对象所在位置的引用。也就是说,当你说
string s = "Hello";
s
不包含 "Hello"
时,它包含存储 "Hello"
的内存地址。
那么当您按值传递参数时会发生什么(C# 中的默认设置)。我们有两种可能性:
参数是一个值类型:你得到一个变量的副本,这意味着
如果你传递 i = 1
你会收到一个 copy 这也
包含 1
,但 两者 都是不同的对象。
这在处理 可变 值类型时很明显,例如 System.Drawing.Point
:
Point point = new Point(0, 0);
Frob(point);
var b = point.X == 1 && point.Y == 1; //False, point does not change.
void Frob(Point p) { p.Offset(1, 1); } // p is a copy of point and therefore contains a copy of the value stored in point, not the value itself.
参数是引用类型:你得到了变量的副本,这意味着你得到了内存地址引用的副本,但是副本指向的对象 是相同的 。这就是你所处的场景。
Foo foo = new Foo();
foo.Blah = 1;
Frob(foo);
var b = foo.Blah == 2; //True, foo.Blah has been modified.
void Frob(Foo f) { foo.Blah = 2; } //both foo and f point to the same object.
请注意,在这两种情况下,您不能做的是修改 引用指向的内容。这行不通:
string s = "hello";
foo(s);
var b = s == "bye"; //false, s still points to the original string
void Foo(string str)
{
str = "bye";
}
现在,如果我们通过引用传递会发生什么?好吧,主要区别在于您传递的是变量本身,而不是副本。这意味着在值类型的情况下,您传递的是原始值,而在引用类型的情况下,您传递的是原始地址,而不是副本。这允许以下内容:
//Value type
Point point = new Point(0, 0);
Frob(ref point);
var b = point.X == 1 && point.Y == 1; //True, point and p are the same variable.
void Frob(ref Point p) { p.Offset(1, 1); }
和
//Value or reference type
string s = "hello";
foo(ref s);
var b = s == "bye"; //true
void Foo(ref string str)
{
str = "bye";
}
希望这能澄清区别。
我在传递 C# 函数参数时遇到问题。
我想知道如何使 C# 函数按值接受参数(以复制原始对象)。
我认为这是 C# 处理这些事情的默认方式,但在以下代码中:
using System;
using System.Collections.Generic;
using System.Linq;
class MaximumElement
{
static void Main(string[] args)
{
Stack<int> numbers = new Stack<int>();
int n = int.Parse(Console.ReadLine());
for (int i = 0; i < n; i++)
{
string input = Console.ReadLine();
switch (input)
{
case "2": numbers.Pop(); break;
case "3": Console.WriteLine(maxElement(numbers)); break;
default:
string[] argz = input.Split(' ');
numbers.Push(int.Parse(argz[1]));
break;
}
}
}
public static int maxElement(Stack<int> stack)
{
int max = stack.Peek();
for (int i = 0; i < stack.Count; i++)
{
if (max >= stack.Peek())
{
stack.Pop();
}
else if (max < stack.Peek())
{
max = stack.Pop();
}
}
return max;
}
}
我的maxElement()
函数实际上改变了我传递给它的原始堆栈,唯一绕过它的方法是手动复制我的堆栈传递给函数内部的函数。
感谢您提前回复 :)
您需要复制 Stack,如果浅拷贝有效,您可以使用 Clone() 方法。
有点复杂。来自 MSDN (https://msdn.microsoft.com/en-us/library/s6938f28.aspx):
A variable of a reference type does not contain its data directly; it contains a reference to its data. When you pass a reference-type parameter by value, it is possible to change the data pointed to by the reference, such as the value of a class member. However, you cannot change the value of the reference itself; that is, you cannot use the same reference to allocate memory for a new class and have it persist outside the block. To do that, pass the parameter using the ref or out keyword. For simplicity, the following examples use ref.
这是他们提供的代码示例:
static void Change(int[] pArray)
{
pArray[0] = 888; // This change affects the original element.
pArray = new int[5] {-3, -1, -2, -3, -4}; // This change is local.
现在,如果您在参数中使用 ref
关键字
static void Change(ref int[] pArray)
{
pArray[0] = 888; // This change affects the original element.
pArray = new int[5] {-3, -1, -2, -3, -4}; // This change also affects the original
所以,考虑到这些,您可以...
public static int maxElement(Stack<int> stack)
{
stack = new Stack<int>(stack); // Now changes will be local
int max = stack.Peek();
for (int i = 0; i < stack.Count; i++)
{
if (max >= stack.Peek())
{
stack.Pop();
}
else if (max < stack.Peek())
{
max = stack.Pop();
}
}
return max;
}
不要将 value 或 reference 的传递参数与 value 类型 和 引用类型。这是初学者常犯的错误,您需要清楚地了解这两者虽然在某种程度上相关,但却是语言的完全不同的特性。
我可能不会使用精确的术语,因为英语不是我的语言,但我希望我能理解这个想法:
- 值类型:变量就是值本身。当您编写以下内容时:
int i = 1;
变量i
保存值1
. - 引用类型:变量是指向内存中对象所在位置的引用。也就是说,当你说
string s = "Hello";
s
不包含"Hello"
时,它包含存储"Hello"
的内存地址。
那么当您按值传递参数时会发生什么(C# 中的默认设置)。我们有两种可能性:
参数是一个值类型:你得到一个变量的副本,这意味着 如果你传递
i = 1
你会收到一个 copy 这也 包含1
,但 两者 都是不同的对象。这在处理 可变 值类型时很明显,例如
System.Drawing.Point
:Point point = new Point(0, 0); Frob(point); var b = point.X == 1 && point.Y == 1; //False, point does not change. void Frob(Point p) { p.Offset(1, 1); } // p is a copy of point and therefore contains a copy of the value stored in point, not the value itself.
参数是引用类型:你得到了变量的副本,这意味着你得到了内存地址引用的副本,但是副本指向的对象 是相同的 。这就是你所处的场景。
Foo foo = new Foo(); foo.Blah = 1; Frob(foo); var b = foo.Blah == 2; //True, foo.Blah has been modified. void Frob(Foo f) { foo.Blah = 2; } //both foo and f point to the same object.
请注意,在这两种情况下,您不能做的是修改 引用指向的内容。这行不通:
string s = "hello"; foo(s); var b = s == "bye"; //false, s still points to the original string void Foo(string str) { str = "bye"; }
现在,如果我们通过引用传递会发生什么?好吧,主要区别在于您传递的是变量本身,而不是副本。这意味着在值类型的情况下,您传递的是原始值,而在引用类型的情况下,您传递的是原始地址,而不是副本。这允许以下内容:
//Value type
Point point = new Point(0, 0);
Frob(ref point);
var b = point.X == 1 && point.Y == 1; //True, point and p are the same variable.
void Frob(ref Point p) { p.Offset(1, 1); }
和
//Value or reference type
string s = "hello";
foo(ref s);
var b = s == "bye"; //true
void Foo(ref string str)
{
str = "bye";
}
希望这能澄清区别。