FxCopが、使い捨てフィールドがないクラスで「使い捨てフィールドを所有するタイプは使い捨てである必要があります」というエラーを発生させるのはなぜですか?
-
10-07-2019 - |
質問
追加のメソッドが追加されたLINQオブジェクトがあります。クラスには使い捨てのプロパティやメソッドはありませんが、FxCopは「使い捨てフィールドを所有するタイプは使い捨てにする必要があります」というエラーを発生させています。そのクラスを参照します。
これまでにコードを減らしてもエラーが表示される:
partial class WikiPage
{
public PagePermissionSet GetUserPermissions(Guid? userId) {
using (WikiTomeDataContext context = new WikiTomeDataContext()) {
var permissions =
from wiki in context.Wikis
from pageTag in context.VirtualWikiPageTags
select new {};
return null;
}
}
}
ただし、from句のいずれかを削除すると、FxCopはエラーの表示を停止します。
partial class WikiPage
{
public PagePermissionSet GetUserPermissions(Guid? userId) {
using (WikiTomeDataContext context = new WikiTomeDataContext()) {
var permissions =
from pageTag in context.VirtualWikiPageTags
select new {};
return null;
}
}
}
または
partial class WikiPage
{
public PagePermissionSet GetUserPermissions(Guid? userId) {
using (WikiTomeDataContext context = new WikiTomeDataContext()) {
var permissions =
from wiki in context.Wikis
select new {};
return null;
}
}
}
PagePermissionSetは使い捨てではありません。
これは誤検知ですか?それとも、LINQコードが何らかの形でクラスの使い捨てフィールドを生成していますか?誤検知でない場合、FxCopはIDisposableインターフェイスを実装することを推奨していますが、Disposeメソッドで何をしますか?
編集: 完全なFxCopエラーは次のとおりです。
"「WikiPage」にIDを実装可能 次のIDisposable型のメンバーを作成します。 「WikiTomeDataContext」。 「WikiPage」が以前に持っている場合 出荷、IDisposableを実装する新しいメンバーを追加 このタイプへの変更は、既存の 消費者。"
編集2: これは、エラーを発生させる逆アセンブルされたコードです。
public PagePermissionSet GetUserPermissions(Guid? userId)
{
using (WikiTomeDataContext context = new WikiTomeDataContext())
{
ParameterExpression CS[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
// Fields
public WikiTomeDataContext context;
// Methods
public <>c__DisplayClass1();
}
$0001;
ParameterExpression CS<*>$0003;
var permissions = context.Wikis.SelectMany(Expression.Lambda<Func<Wiki, IEnumerable<VirtualWikiPageTag>>>(Expression.Property(Expression.Constant(context), (MethodInfo) methodof(WikiTomeDataContext.get_VirtualWikiPageTags)), new ParameterExpression[] { CS<*>$0001 = Expression.Parameter(typeof(Wiki), "wiki") }), Expression.Lambda(Expression.New((ConstructorInfo) methodof(<>f__AnonymousType8..ctor), new Expression[0], new MethodInfo[0]), new ParameterExpression[] { CS<*>$0001 = Expression.Parameter(typeof(Wiki), "wiki"), CS<*>$0003 = Expression.Parameter(typeof(VirtualWikiPageTag), "pageTag") }));
return null;
}
}
編集3: DataContextへの参照を含むクロージャークラスがあるようです。逆アセンブルされたコードは次のとおりです。
<*>解決
2つの From
句は、データコンテキストでクロージャーを使用して SelectMany
の呼び出しを生成すると思います。クロージャーのインスタンスには、FxCop警告の原因となるデータコンテキストへのフィールドがあります。これは心配することではありません。
datacontextのインスタンスは1つのみで、usingブロックを使用してクリーンアップします。クロージャにはファイナライザがないため、FxCop警告にはパフォーマンスや安全性への影響はありません。
他のヒント
これは部分クラスであることに気付きました。クラスの別の実装ファイルを確認し、破棄されていないIDisposableメンバーが含まれているかどうかを確認しましたか?
ここでは、生成されたクロージャーに問題があるとは思わない。 FxCopにこのような警告を無視させる特定の属性を使用してクロージャーが生成されます。
編集
OPによるさらなる調査により、これはIDisposableフィールドが閉鎖されている問題であることが示されました。
残念ながら、これに関してできることはあまりありません。クロージャーにIDisposableを実装させる方法はありません。クロージャーインスタンスでIDisposableを呼び出す方法がない場合のイベント。
この問題に取り組む最良の方法は、使い捨ての値がクロージャーに取り込まれないようにコードを書き直すことです。使い捨てフィールドは、終了時に必ず廃棄する必要があり、クロージャでキャプチャすると、これを実行できなくなります。
メソッドからLINQクエリを返す場合、消費者はforeachを使用して結果を反復処理します。
コンシューマーがforeachループを完了すると、IEnumerableソース(この場合はLINQクエリ)で内部的にdisposeを呼び出します。これにより、WikiTomeDataContextが破棄されます。
ただし、コンシューマーがLINQクエリを返すメソッドを呼び出したが、結果を繰り返し処理しなかった場合、enumerableは破棄されないように見えます(つまり、ガベージコレクターがオブジェクトをクリーンアップするまで)。これにより、WikiTomeDataContextがガベージコレクションまで破棄されなくなります。
この問題を回避できる1つの方法は、LINQクエリの結果で.ToArrayを呼び出し、コンテキストでdisposeを呼び出し、配列を返すことです。
エラーを与えるコードはWikiDataContextを使用します。
エラーを与えない2つの例では、WikiTomeDataContextを使用します。
たぶん、エラーを引き起こしているこれら2つの間にいくつかの違いがあります。