我正在浏览一些使用C#7新功能的代码并使用ref locals&返回功能。
value-types
似乎非常简单,其中ref本地变量获取引用(对于实际存储),并且更新它会更新原始项的值。
在reference-types
的ref本地化的情况下,一点解释将有助于理解内存引用如何工作。我指着下面代码的最后一行:
// 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 }
}
答案 0 :(得分:16)
C#的原始设计师将该功能命名为&#34; ref&#34;在我看来是一个坏主意。它导致人们混淆引用类型和&#34; ref&#34;参数/回报。更好的方式来考虑&#34; ref&#34;是&#34;别名&#34;。也就是说,ref 为您提供了现有变量的另一个名称。
在您的计划中,byRef
是arr[1]
的另一个名称,无论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]
的别名。
再次说明:byRef
只是另一种拼写arr[1]
的方式,因此它始终使用arr
时的值byRef
被分配了。对于值类型或引用类型,它的不不同。
相反,byVal
不是arr[1]
的别名。它是第二个变量,其内容为arr[1]
的副本。当您分配到byVal
时,您未分配给arr[1]
。您已分配给byVal
,这是不同的变量。
arr[1]
的内容是引用,引用被复制到byVal
,一个单独的完全存储位置。
答案 1 :(得分:-1)
另一个有趣的问题是如何强制Ref returns and ref locals
进行测试时的行为?
您可以通过用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);
}
这里是help article,详细说明了测试方法。
免责声明。我是负责JustMock的开发人员之一。