在C#中,我能通过引用传递基本类型吗?

15
我知道在C#中,复杂类型是按引用传递的,而原始类型是按值传递的。那我能否在C#中通过引用传递原始类型呢?
更新:感谢回答,但我的问题是什么?
void test(object x) {

}

long y = 1;

test(ref y);

出现以下异常:“ref”参数类型与参数类型不匹配


7
原始和复杂并不是这里的正确术语。你说的是值类型和引用类型。 - H H
1
你需要拥有 void test (ref object x),因为 ref 需要在方法定义和使用中都被使用。 - Alex
“复杂类型”不会通过引用传递。除非使用ref或out关键字,否则所有类型都是按值传递的。 - Chris Dunaway
1
在函数定义和调用函数时都需要使用 ref - Daniel
可能是C#:为什么'ref'和'out'不支持多态性?的重复问题。 - Eric Lippert
6个回答

22

这里有几个不同的问题。

我能在C#中通过引用传递基元类型吗?

首先,让我们确保术语使用正确。不清楚你所说的“基元类型”指的是什么。你是指内置于运行时的类型,如int或long吗?还是任何值类型,无论是内置的还是用户定义的?

我假设你实际上问的是:

我能在C#中通过引用传递值类型吗?

值类型被称为值类型,因为它们是按值传递的。引用类型被称为引用类型,因为它们是按引用传递的。因此,从定义上看,答案似乎是否定的。

然而,事情并没有那么简单。

首先,你可以通过装箱将值类型的实例转换为引用类型的实例:

decimal d = 123.4m; // 128 bit immutable decimal structure
object o1 = d; // 32/64 bit reference to 128 bit decimal
object o2 = o1; // refers to the same decimal
M(o2); // passes a reference to the decimal.
o2 = 456.78m; // does NOT change d or o1

其次,您可以通过创建一个数组将值类型的实例转换为引用:

decimal[] ds1 = new decimal[1] { 123.4m }; 
decimal[] ds2 = ds1;
ds2[0] = 456.7m; // does change ds1[0]; ds1 and ds2 refer to the same array

其次,您可以使用"ref"关键字传递对变量(而不是值)的引用:

decimal d = 123.4m;
M(ref d);
...
void M(ref decimal x)
{ // x and d refer to the same variable now; a change to one changes the other

试图将 ref long 传递给接受 ref object 的方法会导致编译错误: 'ref' 参数类型与参数类型不匹配

是的,两边变量的类型必须完全一致。有关详细信息,请参见此问题:

为什么不支持多态性的'ref'和'out'?


16

当然可以:

public void MyFunction(ref int a)
{
}

请参阅 http://msdn.microsoft.com/zh-cn/library/14akc2c7(v=vs.71).aspx 上的文档,因为 ref 方法在初始化和重载方面有一些需要注意的地方。 - Lars Tackmann

10

只需使用refout参数修饰符即可。

void myMethod(ref int myValue) ...

ref 是双向的。 out 是单向的。


9
你可以在函数定义中的参数旁边使用ref关键字。
public void func(ref int a)
{
    //do stuff
}

public void doStuf()
{
    int a = 5;
    func(ref a);
}

哎呀,看起来大家都比我快。 - Daniel
3
记住的一条重要规则是变量必须作为 ref 传递。 - Daniel

2
你混淆了概念。默认的参数传递约定是按值传递。你可以将引用类型对象传递给方法,也可以将值类型对象传递给方法。你可以使用变量来传递对象。这个变量是按值传递的。在方法内部,参数是你传递的变量的一个副本。
如果你用 refout 修饰参数,则在方法内部更改该参数也将影响传递的变量。
// Doesn't change obj1
void DoesntChange(object obj)
{
    obj = new object(); // Assigns a new object to the local variable obj.
}

var obj1 = new object();
DoesntChange(obj1);

// Will change obj2
void WillChange(ref object obj)
{
    obj = new object(); // Assigns a new object to the local ref variable obj
                        // and changes the obj2 variable to point at the new object.
}

var obj2 = new object();
WillChange(ref obj2);

如果要在方法内部改变一个基本类型变量的参数并在调用点保持更改,必须通过refout传递。


1

更新后,您可以通过引用传递封装的值

static void Main(string[] args)
{
    var value = 10;
    var boxed = (object)value;
    TakesValueByRef(ref boxed);
    value = (int)boxed;
    // value now contains 5.
}

static void TakesValueByRef(ref object value)
{
    value = 5;
}

请记住,这种做法很糟糕,并且存在风险(由于类型转换异常)。


这个示例与上面的注释不匹配。你使用了'ref'关键字,所以你是通过引用传递的。这与装箱无关。你可以通过转换为对象或仅使用'ref'关键字来进行装箱,不需要两者都做。 - justin.m.chase
@justin.m.chase 先在 IDE 中尝试你的理论。如果你看他的问题,他想要一个对象参数;这涉及到装箱,这意味着你需要在将其传递给函数之前有一个装箱值。 - Jonathan Dickinson
@johnathan:嗯,他的问题在代码和术语上都有一些错误。首先,他的代码甚至无法编译。说实话,我甚至不理解你的回答。你不需要装箱来通过引用传递值类型。就这么简单。尝试“void test(ref long value) { value = 5; }”,你会看到的。 - justin.m.chase
@justin.m.chase 如果该方法需要一个对象参数,则您需要将其装箱。 - Jonathan Dickinson
1
@justin.m.chase,我觉得你批评一个明显你自己也不理解的答案是很滑稽的。你这么做让我很开心。 - Engineer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接