C# 强制函数输入参数不可变?
我刚刚花了2天的大部分时间试图追踪一个bug,结果我意外地改变了作为函数输入提供的值C# 强制函数输入参数不可变?,c#,function,parameters,immutability,C#,Function,Parameters,Immutability,我刚刚花了2天的大部分时间试图追踪一个bug,结果我意外地改变了作为函数输入提供的值 IEnumerable<DataLog> FilterIIR( IEnumerable<DataLog> buffer ) { double notFilter = 1.0 - FilterStrength; var filteredVal = buffer.FirstOrDefault()?.oilTemp ??
IEnumerable<DataLog>
FilterIIR(
IEnumerable<DataLog> buffer
) {
double notFilter = 1.0 - FilterStrength;
var filteredVal = buffer.FirstOrDefault()?.oilTemp ?? 0.0;
foreach (var item in buffer)
{
filteredVal = (item.oilTemp * notFilter) + (filteredVal * FilterStrength);
/* Mistake here!
item.oilTemp = filteredValue;
yield return item;
*/
// Correct version!
yield return new DataLog()
{
oilTemp = (float)filteredVal,
ambTemp = item.ambTemp,
oilCond = item.oilCond,
logTime = item.logTime
};
}
}
IEnumerable
过滤器(
IEnumerable缓冲区
) {
双notFilter=1.0-过滤器强度;
var filteredVal=buffer.FirstOrDefault()?.oilTemp±0.0;
foreach(缓冲区中的变量项)
{
filteredVal=(item.oilTemp*非过滤器)+(filteredVal*过滤器强度);
/*这里错了!
item.oilTemp=过滤数据值;
收益回报项目;
*/
//正确的版本!
返回新的数据日志()
{
油温=(浮子)过滤器深度,
ambTemp=项目ambTemp,
oilCond=项目。oilCond,
logTime=item.logTime
};
}
}
<>我的首选编程语言通常是C或C++,这取决于我认为更适合要求的(这是一个更适合C语言的更大程序的一部分)……
ireadonlynumerable
类,该类将IEnumerable
作为构造函数,但后来我意识到,除非它在检索时复制值,否则实际上不会产生任何效果,因为基础值仍然可以修改
我有没有办法在将来避免这些错误?一些包装类,或者即使它是我想要保护的每个函数顶部的一个小代码段,任何东西都可以
目前我能想到的唯一可行的方法是为我需要的每个类定义一个
ReadOnly
版本,然后创建一个非只读版本,该版本继承和重载属性,并添加函数以提供同一类的可变版本。问题在于这里并不是关于IEnumerable
IEnumerable
s实际上是不可变的。您不能添加或删除其中的内容。可变的是您的数据日志
类
因为DataLog
是引用类型,item
保存对原始对象的引用,而不是对象的副本。这一点,加上DataLog
是可变的,允许您对传入的参数进行变异
因此,在高层次上,您可以:
- 复制
,或李>数据日志
- 使
不可变DataLog
数据日志
”。另一种方法是将DataLog
从class
更改为struct
。这样,在将其传递给方法时,您将始终创建其副本(除非您使用ref
标记参数)。因此,在使用此方法时要小心,因为它可能会悄悄地破坏假定按引用传递语义的现有方法
您还可以使DataLog
不可变。这意味着删除所有设置器。或者,您可以添加名为的方法WithXXX
,该方法返回仅具有一个不同属性的对象副本。如果您选择执行此操作,则您的FilterIIR
将如下所示:
yield return item.WithOilTemp(filteredVal);
目前我能想到的唯一可行的方法是为我需要的每个类定义一个只读版本,然后使用一个非只读版本来继承和重载属性,并添加函数以提供同一类的可变版本
你实际上不需要这样做。请注意,
列表
显然是可变的。您可以编写一个名为IReadOnlyDataLog
的接口。此接口将只有数据日志的getter
。然后,让FilterIIR
接受IEnumerable
和DataLog
实现IReadOnlyDataLog
。这样,您就不会意外地改变FilterIIR
中的DataLog
对象。您是否考虑过将DataLog
作为一个不可变的类。感谢@Salah的格式检查,尽管实现了
并不是一个错误XD@juharr我在最后一段提到,除非这不是你的意思?我是说拥抱不可变,不要为类的可变版本而烦恼。@TheBeardedQuack,不客气。你是对的,这不是一个错误,但是拼写检查建议使用“已实现”,这就是我更改它的原因:)Hi@Sweeper,我没有想到使用接口方法,但这实际上就是我创建只读基类然后继承的意思。接口方法更整洁,因为我只有一个包含实现的位置。我知道,IEnumerable
已经是不可变的了,也许我本可以更好地解释自己。当我在C++中有一个IReadOnlyDataLog
方法总体上也是最好的