我正在创建一个函数,我需要传递一个对象,以便它可以被函数修改。有什么区别:
public void myFunction(ref MyClass someClass)
and
public void myFunction(out MyClass someClass)
我应该用哪个,为什么?
我正在创建一个函数,我需要传递一个对象,以便它可以被函数修改。有什么区别:
public void myFunction(ref MyClass someClass)
and
public void myFunction(out MyClass someClass)
我应该用哪个,为什么?
当前回答
我可能不太擅长这一点,但肯定字符串(即使他们在技术上是引用类型和生活在堆上)是通过值传递,而不是引用?
string a = "Hello";
string b = "goodbye";
b = a; //attempt to make b point to a, won't work.
a = "testing";
Console.WriteLine(b); //this will produce "hello", NOT "testing"!!!!
这就是为什么你需要引用,如果你想要改变存在于函数的作用域之外,否则你不传递引用。
据我所知,你只需要引用结构/值类型和字符串本身,因为字符串是一个引用类型,假装它是,但不是一个值类型。
我可能完全错了,我是新来的。
其他回答
裁判上场又出局。
您应该优先使用out,只要它能满足您的需求。
如果你想将参数作为引用传递,那么你应该在将参数传递给函数之前初始化它,否则编译器本身会显示错误。但是对于out形参,您不需要在将对象形参传递给方法之前初始化它。可以在调用方法本身中初始化对象。
有两个主要的区别,我想举例说明:
Ref和out通过reference传递,hense;
class Program
{
public static void Main(string[] args)
{
var original = new ObjectWithMememberList(3);
Console.WriteLine(original.MyList.Capacity); // 3
ChangeList(original.MyList);
Console.WriteLine(original.MyList.Capacity); // 3
}
static void ChangeList(List<int> vr)
{
vr = new List<int>(2);
}
}
but:
class Program
{
public static void Main(string[] args)
{
var original = new ObjectWithMememberList(3);
Console.WriteLine(original.MyList.Capacity); // 3
ChangeList(ref original.MyList);
Console.WriteLine(original.MyList.Capacity); // 2
}
static void ChangeList(ref List<int> vr)
{
vr = new List<int>(2);
}
}
out也是一样。 2. Ref参数必须是一个可赋值变量。 hense:
ChangeList(ref new List<int>()); // Compile Error [might not be initialized before accessing]
but:
List<int> xs;
ChangeList(out xs); // Compiles
创作时间:
我们创建调用方法Main()
(2)它创建一个List对象(这是一个引用类型对象)并将其存储在变量myList中。
public sealed class Program
{
public static Main()
{
List<int> myList = new List<int>();
在运行时:
(3)运行时在堆栈#00处分配一个内存,足够宽来存储一个地址(#00 = myList,因为变量名实际上只是内存位置的别名)
(4)运行时在内存位置#FF的堆上创建一个列表对象(所有这些地址都是为了举例)
(5) Runtime会将对象的起始地址#FF存储在#00(或者在word中,将List对象的引用存储在指针myList中)
回到创作时间:
(6)然后我们将List对象作为参数myParamList传递给被调用的方法modifyMyList,并将一个新的List对象赋给它
List<int> myList = new List<int>();
List<int> newList = ModifyMyList(myList)
public List<int> ModifyMyList(List<int> myParamList){
myParamList = new List<int>();
return myParamList;
}
在运行时:
(7)运行时启动被调用方法的调用例程,作为它的一部分,检查参数的类型。
(8)在找到引用类型后,它在堆栈#04处分配一个内存,用于别名参数变量myParamList。
然后它将值#FF也存储在其中。
(10) Runtime在内存位置#004的堆上创建一个列表对象,并用这个值替换#04中的#FF(或者在这个方法中取消引用原始的list对象并指向新的list对象)
#00中的地址不会改变,并保留对#FF的引用(或者原始的myList指针不会被干扰)。
ref关键字是一个编译器指令,用于跳过(8)和(9)的运行时代码的生成,这意味着不会为方法参数分配堆。它将使用原始的#00指针对位于#FF的对象进行操作。如果原始指针没有初始化,运行时将停止抱怨它不能继续,因为变量没有初始化
out关键字是一个编译器指令,它与ref几乎相同,只是在(9)和(10)处略有修改。编译器期望参数是未初始化的,并将继续使用(8),(4)和(5)在堆上创建一个对象,并将其起始地址存储在参数变量中。不会抛出任何未初始化的错误,并且之前存储的任何引用都将丢失。
我要试着解释一下:
我想我们理解了值类型是如何工作的,对吧?值类型是(int, long, struct等)。当你将它们发送到一个没有ref命令的函数时,它会复制数据。在函数中对该数据所做的任何操作都只会影响副本,而不会影响原始数据。ref命令发送的是实际数据,任何变化都会影响函数外部的数据。
好了,让人困惑的部分,引用类型:
让我们创建一个引用类型:
List<string> someobject = new List<string>()
当你新建someobject时,会创建两部分:
存储某对象数据的内存块。 指向该块的引用(指针) 的数据。
现在,当你发送someobject到一个没有引用的方法时,它复制的是引用指针,而不是数据。现在你得到了这个:
(outside method) reference1 => someobject
(inside method) reference2 => someobject
两个引用指向同一个对象。如果你使用reference2修改某个对象的属性,它将影响由reference1指向的相同数据。
(inside method) reference2.Add("SomeString");
(outside method) reference1[0] == "SomeString" //this is true
如果你清空了reference2或将其指向新的数据,它不会影响reference1和reference1所指向的数据。
(inside method) reference2 = new List<string>();
(outside method) reference1 != null; reference1[0] == "SomeString" //this is true
The references are now pointing like this:
reference2 => new List<string>()
reference1 => someobject
当你将someobject by ref发送给方法时会发生什么? 对某个对象的实际引用被发送给方法。所以你现在只有一个数据引用:
(outside method) reference1 => someobject;
(inside method) reference1 => someobject;
但这意味着什么呢?它的作用与不通过ref发送someobject完全相同,除了两件主要的事情:
1)当你清空方法内部的引用时,它也会清空方法外部的引用。
(inside method) reference1 = null;
(outside method) reference1 == null; //true
2)你现在可以将引用指向一个完全不同的数据位置,函数外部的引用现在将指向新的数据位置。
(inside method) reference1 = new List<string>();
(outside method) reference1.Count == 0; //this is true