C# 为什么可以';我是否向函数传递不同数量的引用?
我想这样做:C# 为什么可以';我是否向函数传递不同数量的引用?,c#,.net,params,ref,C#,.net,Params,Ref,我想这样做: double a, b, c, d, e; ParseAndWrite("{1, 2, 3}", ref a, ref b, ref c); ParseAndWrite("{4, 5}", ref d, ref e); -> a = 1, b = 2, c = 3, d = 4, e = 5 private void ParseAndWrite(string leInput, params ref double[] targets) { (...) } input
double a, b, c, d, e;
ParseAndWrite("{1, 2, 3}", ref a, ref b, ref c);
ParseAndWrite("{4, 5}", ref d, ref e);
-> a = 1, b = 2, c = 3, d = 4, e = 5
private void ParseAndWrite(string leInput, params ref double[] targets)
{
(...)
}
inputConfig : " step, stepHeight, rStep, rStepHeight, (nIterations, split, smooth) "
outputConfig : " dataSelection, (corrected, units, outlierCount, restoreOriginalRange) "
但是,我不能编写这样的函数:
double a, b, c, d, e;
ParseAndWrite("{1, 2, 3}", ref a, ref b, ref c);
ParseAndWrite("{4, 5}", ref d, ref e);
-> a = 1, b = 2, c = 3, d = 4, e = 5
private void ParseAndWrite(string leInput, params ref double[] targets)
{
(...)
}
inputConfig : " step, stepHeight, rStep, rStepHeight, (nIterations, split, smooth) "
outputConfig : " dataSelection, (corrected, units, outlierCount, restoreOriginalRange) "
这不起作用,因为某些原因,不能同时使用ref和params。为什么会这样
编辑:好的,这里有一些关于我为什么需要这个的更多信息:通过一个接口,我得到了很多包含值的字符串,语法如下:
double a, b, c, d, e;
ParseAndWrite("{1, 2, 3}", ref a, ref b, ref c);
ParseAndWrite("{4, 5}", ref d, ref e);
-> a = 1, b = 2, c = 3, d = 4, e = 5
private void ParseAndWrite(string leInput, params ref double[] targets)
{
(...)
}
inputConfig : " step, stepHeight, rStep, rStepHeight, (nIterations, split, smooth) "
outputConfig : " dataSelection, (corrected, units, outlierCount, restoreOriginalRange) "
(括号中的名称是可选的)。这些值需要解析并存储在所有特定变量中。也就是说,它们根本不是数组。它们更像是命令行参数,但大约有20个。当然,我可以按顺序完成所有这些工作,但这会产生数百行代码,其中包含冗余模式,并且无法很好地维护 人们可以问很多“为什么语言X不能做Y”类型的问题。大多数情况下,答案是因为可能不太需要该功能
一个可变的ref参数集让人觉得很奇怪,我无法想象它会有用,除非在某些有限的情况下,而且通常会有更好的抽象来返回项目列表
当然,您可以传递一个预大小的
double[]
来填充方法,尽管我的偏好是只返回一个IEnumerable
我不知道这是否是唯一的原因,但这里有一个可能的解释:
实际参数最终仍然是一个double[]
,因此为了完成您想要的操作,编译器实际上需要将其编写为:
{
double[] arr = new double[] {a, b, c, d}; // copy in
YourMethod(arr);
a = arr[0]; // copy back out
b = arr[1];
c = arr[2];
d = arr[3];
}
然而!这改变了语义。假设调用方法实际上是:
void SomeCallingMethod(ref double a)
{
double b = ..., c = ... d = ...;
YourMethod(ref a, ref b, ref c, ref d);
/* which, say, is translated to
{
double[] arr = new double[] {a, b, c, d}; // copy in
YourMethod(arr);
a = arr[0]; // copy back out
b = arr[1];
c = arr[2];
d = arr[3];
} */
}
预期的行为是我们总是直接更新a
,但实际上我们不是。这可能会导致奇怪和混乱的错误。事实上,为了正确地执行此操作,“复制回”需要进入最后一个,因为实际上ref
值应该在异常之前显示更改,但这仍然不会改变在方法调用之后更新而不是在异常期间更新的语义
您还会遇到一个非常奇怪的问题:更新数组会更新调用方多长时间?毕竟,数组可以存储在任何地方,因为它不在堆栈上
还有另一种发送方法,作为对值的引用数组,但这可能是非常致命的:
- 这将使代码本质上不安全
- 如果数组存储在任何位置(可以是哪些数组),则调用方法可以返回正在使用的本地并取消其作用域,这意味着将有一个看起来有效的引用,它实际上指向垃圾严重的损坏
总而言之,这样做没有什么好处,也有很多坏处。原因可能是技术上的params'只是数组参数的语法糖。因此,ParseAndWrite(string-leInput,params-double[]targets)
被编译为ParseAndWrite(string-leInput,params-double[]targets)
然后,ParseAndWrite(string-leInput,params-ref-double[]目标)
将被编译为ParseAndWrite(string-leInput,ref-double[]目标)
。基本上,作为引用传递的是数组,而不是数组的实际内容。这显然不是开发人员期望的行为,因此容易出错
由于某些原因,不能同时使用ref和params。为什么会这样
考虑摄像机的这三个理想特性:
轻量级
高质量透镜
便宜的
在任何摄像机中,你最多只能看到两个这样的镜头。你永远都得不到这三个。你可以买到便宜的重型大镜头相机,或者昂贵的轻型大镜头相机,或者便宜的轻型快照相机,但是没有便宜的轻型大镜头相机
现在回到你的问题。考虑运行时的这三个需要的特性:
引用变量类型的参数数组是合法的
类型系统是安全的
具有引用的局部变量仍然可以快速分配和取消分配
你可以有任何两个,但你不能有全部三个。你想要哪两个
您可以拥有ref-param数组和安全类型系统,代价是在垃圾收集堆上分配ref'd局部变量。据我所知,没有人这样做,但这肯定是可能的
您可以拥有ref-param数组、临时池上分配的所有局部变量,以及错误使用时崩溃的类型系统。这就是“C/C++”方法;可以获取引用并将其存储在比被引用对象的生命周期更长的位置。如果你这样做,你可以期待你的C++程序崩溃,并以最可怕的方式死去,因为容易犯的错误很难被发现。欢迎来到绝望的深渊
或者我们可以使ref-param数组非法,在临时池上分配所有局部变量,并使用可验证内存安全的类型系统。这是我们在构建C#和CLR时做出的选择;欢迎来到质量坑
现在,我上面说的其实都是一个大谎言。这是一个谎言,因为运行时和C#语言确实支持类似于(但不完全相同)REF参数数组的功能。这是一个令人愉快的谎言,相信我,你想生活在一个你相信谎言的世界里,所以我建议你服用蓝色药丸并继续这样做
如果您想服用红色药丸并了解兔子洞有多深,您可以使用C的未记录的\uu arglist
功能构建C型变量方法,然后创建引用refe的类型引用
对象