提问人:Arghya C 提问时间:1/18/2018 最后编辑:Cody Gray - on strikeArghya C 更新时间:2/14/2019 访问量:2127
引用类型的 C# 7 引用返回
C# 7 ref return for reference types
问:
我正在浏览一些使用 C# 7 新功能并使用 ref locals & returns 功能的代码。
对于 ref 局部变量获取引用(对实际存储)的引用,以及更新原始项的值,这似乎很简单。value-types
一些解释将有助于理解内存引用在 的 ref 局部变量的情况下是如何工作的。我指向下面代码的最后一行:reference-types
// A simple class
public class CoolClass
{
public string Name { get; set; }
public int Id { get; set; }
public CoolClass(string name, int id) => (Name, Id) = (name, id);
}
//Dummy method that returns first element with Id > 100
public CoolClass GetSpecialItem_Old(CoolClass[] arr)
{
for (int i = 0; i < arr.Length; i++)
if (arr[i].Id > 100)
{
return arr[i];
}
throw new Exception("Not found");
}
//Same as the other one, but returns by ref C# 7
public ref CoolClass GetSpecialItem_New(CoolClass[] arr)
{
for (int i = 0; i < arr.Length; i++)
if (arr[i].Id > 100)
{
return ref arr[i];
}
throw new Exception("Not found");
}
public void TestMethod()
{
var arr = new CoolClass[]
{
new CoolClass("A", 10),
new CoolClass("B", 101),
new CoolClass("C", 11)
};
var byVal = GetSpecialItem_Old(arr); //gets back arr[1]
byVal.Name = "byVal"; //arr[1] = { "byVal", 101 }
byVal = new CoolClass("newByVal", 25); //arr[1] = { "byVal", 101 }
ref var byRef = ref GetSpecialItem_New(arr); //gets back arr[1]
byRef.Name = "byRef"; //arr[1] = { "byRef", 101 }
//Here it has a different behaviour
byRef = new CoolClass("newByRef", 50); //arr[1] = { "newByRef", 50 }
}
答:
在我看来,C# 的原始设计者将该功能命名为“ref”是一个坏主意。它导致人们混淆引用类型和“ref”参数/返回。考虑“ref”的更好方法是“别名”。也就是说,ref 为现有变量提供了另一个名称。
在程序中,无论 arr[1]
是值类型还是引用类型,都是另一个名称。如果是一个字符串变量(记住,数组元素是变量,你可以改变它们),那么也是一个字符串变量,它是同一个字符串变量,只是名称不同。byRef
arr[1]
arr[1]
byref
请注意,这也是一个变量;如果更改 的值,则不会随车而来。它仍然是同一数组的同一插槽的别名,而不考虑 的值。arr
arr
byRef
arr
所以当你说
ref var byRef = ref GetSpecialItem_New(arr); //gets back arr[1]
然后
byRef.Name = "byRef";
和
arr[1].Name = "byRef";
当你说
byRef = new CoolClass("newByRef", 50);
这和
arr[1] = new CoolClass("newByRef", 50);
需要注意的是,如果您在分配后进行了更改,则仍然会有一个原始的别名。arr
byRef
arr[1]
再次: 只是拼写 arr[1]
的另一种方式,因此它总是使用它在赋值时所拥有的值。对于值类型或引用类型,它没有区别。byRef
arr
byRef
相反,不是 的别名。它是第二个变量,具有 的内容副本。当您指派给您时,您不是指派给 。您要分配给 ,这是一个不同的变量。byVal
arr[1]
arr[1]
byVal
arr[1]
byVal
的内容是一个引用,并且该引用被完全复制到一个单独的存储位置。arr[1]
byVal
评论
better way to think of "ref" is "alias"
such that it always uses the value of arr that it had when byRef was assigned
另一个有趣的问题是,在测试时,如何强制 Ref 返回和 ref 局部
变量按照您的意愿行事?
您可以通过使用 JustMock 模拟该方法来做到这一点。GetSpecialItem_New
以下是问题中的方法:
public class Foo
{
//Same as the other one, but returns by ref C# 7
public ref CoolClass GetSpecialItem_New(CoolClass[] arr)
{
for (int i = 0; i < arr.Length; i++)
if (arr[i].Id > 100)
{
return ref arr[i];
}
throw new Exception("Not found");
}
}
下面介绍如何模拟该方法,以返回隔离测试的所需结果:
[TestMethod]
public void TestCoolClass()
{
var expected = new CoolClass("42", 42);
var arr = new CoolClass[]
{
new CoolClass("A", 10),
new CoolClass("B", 101),
new CoolClass("C", 11)
};
// Arrange
var sut = Mock.Create<Foo>();
Mock.Arrange(sut, s => s.GetSpecialItem_New(arr)).Returns(LocalRef.WithValue(expected).Handle).OccursOnce();
// Act
ref CoolClass actual = ref sut.GetSpecialItem_New(arr);
// Assert
Assert.AreEqual(expected, actual);
}
这是一篇帮助文章,详细解释了测试方法。
免責聲明。我是负责 JustMock 的开发人员之一。
评论
ref var byRef
基本上是相同的引用,所以如果你“实际上”在引擎盖下做。arr[1]
byRef = new ...
arr[1] = new ...