linq C# IEnumerable在函式参数变更后传回变更

tsm1rwdh  于 2022-12-06  发布在  C#
关注(0)|答案(2)|浏览(110)

我在编写单元测试时使用请求对象作为参数来比较原始响应和过滤后的响应。在这样做的过程中,我注意到,如果我在获得响应后更改请求对象,IEnumerable列表也会更改-当我键入此命令时,我的想法是,因为它是带有LINQ的IEnumerable,所以request.Filter属性是LINQ查询中的引用,这就是导致此行为的原因。如果我将其转换为列表而不是IEnumerable,我怀疑此行为将消失,因为.ToList()将计算LINQ表达式而不是延迟。是这样吗?

public class VendorResponse {
    public IEnumerable<string> Vendors { get; set; }
}

var request = new VendorRequest() {
    Filter = ""
};

var response = await _service.GetVendors(request);

int vendorCount = response.Vendors.Count(); // 20
request.Filter = "at&t";
int newCount = response.Vendors.Count(); // 17

public async Task<VendorResponse> GetVendors(VendorRequest request)
{
    var vendors = await _dataService.GetVendors();
    return new VendorResponse {
        Vendors = vendors.Where(v => v.IndexOf(request.Filter) >= 0)
    }
}
hzbexzde

hzbexzde1#

如果更喜欢延迟执行,则可以使用局部变量捕获request.Filter的当前状态,并在Where predicate 中使用该状态

public async Task<VendorResponse> GetVendors(VendorRequest request)
{
    var filter = request.Filter;

    var vendors = await _dataService.GetVendors();

    return new VendorResponse {
        Vendors = vendors.Where(v => v.IndexOf(filter) >= 0)
    }
}
u4dcyp6a

u4dcyp6a2#

是的!是的!
这是IEnumerable的deferred execution示例,它只封装对某些数据的查询,而不封装该查询的结果。
IEnumerable可以被枚举(通过它的IEnumerator),并且“知道”如何枚举它封装的查询,但这在执行枚举之前不会真正发生。
在您的示例中,枚举是通过调用.Count()来执行的,.Count()需要知道查询结果中有多少项。每次调用.Count()时都会发生枚举,因此在两次调用之间更改过滤器会导致您得到两个不同的结果。
正如您正确推断的那样,在执行任何进一步的操作之前调用.ToList()并在变量中捕获结果将导致您捕获结果数据而不是查询,因此导致两个计数具有相同的值。
您自己试试看。以后,一定要在传递到其他查询或返回到未知代码之前强制计算可枚举对象,否则您或您的用户将遇到意外行为和可能的性能问题。
希望这对你有帮助:)
编辑1:正如Moho has pointed out,您在最初的帖子中也提到过,这也是IEnumerable捕获request.Filter作为引用类型的结果。如果您可以捕获该值并将其传递进来,IEnumerable的结果将不再通过更改过滤器来修改。

相关问题