我在Select
对象中创建了IDisposable
的linq。之后有一个过滤器Where
,它导致某些对象从未处置。
这是一个复制人:
class Program
{
static void Main(string[] args)
{
var results = "1234567890"
.Select(o => new Test(o))
.Where(o => o.Value > '3' && o.Value < '7')
.ToList();
// do something with results
// ...
// dispose
foreach (var result in results)
result.Dispose();
}
}
class Test : IDisposable
{
public char Value { get; }
public Test(char value)
{
Value = value;
Console.WriteLine($"{Value}");
}
public void Dispose() => Console.WriteLine($"{Value} disposed");
}
输出:
1
2
3
4
5
6
7
8
9
0
4 disposed
5 disposed
6 disposed
问题:
您会看到1
,2
,3
,7
,8
,9
,0
它们是创建的,永远不会被丢弃。
我的解决方案:
我可以在Where
内移动Select
条件,但是然后我需要应用难看的“返回null
+ Where
而不是null
”解决方法:>
var results = "1234567890".Select(o =>
{
if (o > '3' && o < '7')
return new Test(o);
return null;
}).Where(o => o != null).ToList();
输出:
4
5
6
4 disposed
5 disposed
6 disposed
有没有一种更好的方式(更优雅)?
如果Select
处于某个返回IEnumerable<T>
的库方法中,而我无法更改,那么我的解决方法很难做到。如何应用Where
而不会泄漏?
答案 0 :(得分:1)
您会看到创建了1,2,3,7,8,9,0 从来没有处置过
这是因为Where
需要为每个Test
调用创建Select
实例以根据条件测试结果。
过滤后,results
包含已创建对象的子集=>您的代码仅处理此子集项。
有没有更好(更优雅)的方式?
唯一的 LINQ 方法是在过滤之前,将初始可枚举的实例化成List<Test>
(或数组),并处理列表项:
var results = "1234567890"
.Select(o => new Test(o))
.ToList();
var filteredResults = results
.Where(o => o.Value > '3' && o.Value < '7')
.ToList();
// do something with FILTERED results
// ...
// dispose
foreach (var result in results)
result.Dispose();
但是,如果出于某些原因需要尽快Dispose
产生不必要的结果,并且您无法修改代码,则会产生可枚举的结果,只是不要使用LINQ 。编写常规的foreach
:
var enumerable = "1234567890"
.Select(o => new Test(o));
var results = new List<Test>();
foreach (var item in enumerable)
{
if (!(item.Value > '3' && item.Value < '7'))
{
item.Dispose();
}
else
{
results.Add(item);
}
}
// do something with results
// ...
// dispose
foreach (var result in results)
result.Dispose();
答案 1 :(得分:0)
从库中获取项目后,可以将它们存储在数组中。您可以过滤数组,对相关项进行处理,然后使用数组处理所有项:
DETAILS: Can't find method com.mirth.connect.server.userutil.ChannelMap.size(string).
at 7b0f55d4-9758-4764-8486-6b0363f598c5:75 (doTransform)
at 7b0f55d4-9758-4764-8486-6b0363f598c5:101 (doScript)
at 7b0f55d4-9758-4764-8486-6b0363f598c5:103
at com.mirth.connect.server.transformers.JavaScriptFilterTransformer$FilterTransformerTask.doCall(JavaScriptFilterTransformer.java:154)
at com.mirth.connect.server.transformers.JavaScriptFilterTransformer$FilterTransformerTask.doCall(JavaScriptFilterTransformer.java:119)
at com.mirth.connect.server.util.javascript.JavaScriptTask.call(JavaScriptTask.java:113)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
import java.util.Random; {
public static void main(String[] args) {
int randomNr;
boolean[] lottoRow = new boolean[35];
Random randNr = new Random();
for(int i=0; i<7; i++) {
randomNr = randNr.nextInt(35)+1;
}
}
将项目存储在一个数组中,这样您就可以在处理它们时再次处理相同的项目,而不必再次查询库,这可能会导致返回新的项目。
如果您担心内存不足,可以使用以下(丑陋的)方法尽早丢弃不必要的项目。这可能是一个优势,但是与使用var items = GetItemsFromLibrary().ToArray();
try
{
var relevantItems = items.Where(o => o.Value > '3' && o.Value < '7');
// Do something with relevant items
// ...
}
finally
{
// Dispose all items
foreach (var item in items)
item.Dispose();
}
存储所有项目相比,我怀疑这不是一个很大的优势。
ToArray
即使在ToArray
子句中放置不必要的项目时,该对象也已在库中实例化。因此,最好的选择是将库更改为仅返回相关项目。
答案 2 :(得分:0)
您需要记住所有项目:
var allItems= "1234567890"
.Select(o => new Test(o)).ToArray();
var result = allItems.Where(o => o.Value > '3' && o.Value < '7')
.ToList();
而不是处理allItems
foreach (var result in allItems)
result.Dispose();
答案 3 :(得分:0)
您可以先用string
过滤char
,然后选择所需的类型:
var results = "1234567890".Where(ch => ch > '3' && ch < '7').Select(s => new Test(s)).ToList();
foreach (var item in results)
{
item.Dispose();
}
答案 4 :(得分:0)
正如其他人所述,您正在创建对象,然后丢弃对它们的引用,这意味着无法调用Dispose()
。
这些对象最终将在将来的某个时候由GC收集,但是GC不会不会在没有一点帮助的情况下自动为您调用Dispose()
。
如果这是您真正需要的模式,则可以在一次性对象上实现终结器,以在GC启动时为您调用Dispose()
。
class Program
{
static void Main(string[] args)
{
var results = "1234567890"
.Select(o => new Test(o))
.Where(o => o.Value > '3' && o.Value < '7')
.ToList();
// do something with results
// ...
// dispose
foreach (var result in results)
result.Dispose();
// Force GC to prove dispose called...
GC.Collect();
Console.ReadLine();
}
}
class Test : IDisposable
{
public char Value { get; }
public Test(char value)
{
Value = value;
Console.WriteLine($"{Value}");
}
public void Dispose() => Console.WriteLine($"{Value} disposed");
~Test()
{
Dispose();
}
}