次の例外の原因は何ですか?
System.MissingMethodException Int32 System.Environment.get_CurrentManagedThreadId()
このメソッド呼び出しは、メソッドを生成するために C# コンパイラによって生成されたようIEnumerable<>
です。
.NET Framework v4.0 x86 がインストールされ、バイナリは v4.0 Any CPU 用にコンパイルされます。
CurrentManagedThreadId
は .NET 4.5 プロパティであるため、コードを実行するには 4.5 が必要です。この問題がどのように発生するかの分析については、Iterator ブロック、不足しているメソッド、および .NET 4.5を参照してください。
要するに:
.NET 4.5 がインストールされているシステムでアプリケーション (.NET 4.0 を対象とする) をビルドする場合、.NET 4.0 Framework は常に .NET 4.5 によって上書きされるため、コンパイルのベースとして 4.5 が使用されます。
アプリケーションで も使用する場合、yield return
4.0 のみがインストールされているシステムでは失敗します。これは、このステートメントの実装が 4.5 Framework 用にコンパイルされたときに新しいプロパティを使用するためです。
これを解決するには、コンパイラ システムに 4.0 参照アセンブリがあることを確認してください。
セコンド floeleの答え; 詳細なコンテキストについては、問題の簡単な分析を次に示します。
コンパイラは、 を返す反復子ブロックを処理するときに、反復ロジックを保持するIEnumerable
プライベート クラスを生成します。これは、4.0 コンパイラによってそのメソッドIEnumerable
用に生成された IL の冒頭です。GetEnumerator
.method private final hidebysig newslot virtual
instance class [mscorlib]System.Collections.Generic.IEnumerator`1<string> 'System.Collections.Generic.IEnumerable<System.String>.GetEnumerator' () cil managed
{
.custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = (
01 00 00 00
)
.override method instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<string>::GetEnumerator()
// Method begins at RVA 0x57848
// Code size 89 (0x59)
.maxstack 6
.locals init (
[0] bool,
[1] class DOT.Core.MiscHelpers/'<ReadLines>d__0',
[2] class [mscorlib]System.Collections.Generic.IEnumerator`1<string>
)
IL_0000: call class [mscorlib]System.Threading.Thread [mscorlib]System.Threading.Thread::get_CurrentThread()
IL_0005: callvirt instance int32 [mscorlib]System.Threading.Thread::get_ManagedThreadId()
IL_000a: ldarg.0
IL_000b: ldfld int32 DOT.Core.MiscHelpers/'<ReadLines>d__0'::'<>l__initialThreadId'
IL_0010: bne.un IL_0027
System.Threading.Thread::get_CurrentThread()
との呼び出しに注意してくださいSystem.Threading.Thread::get_ManagedThreadId();
。生成されたメソッドはこれを使用して最適化を実行します。IEnumerable
がすぐに消費される場合 [1]、同じオブジェクト インスタンスが返されます (コンストラクター呼び出しのコストを節約します)。
以下は、4.5 コンパイラによって生成された IL です。
.method private final hidebysig newslot virtual
instance class [mscorlib]System.Collections.Generic.IEnumerator`1<string> 'System.Collections.Generic.IEnumerable<System.String>.GetEnumerator' () cil managed
{
.custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = (
01 00 00 00
)
.override method instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<string>::GetEnumerator()
// Method begins at RVA 0x4830c
// Code size 64 (0x40)
.maxstack 2
.locals init (
[0] class DOT.Core.MiscHelpers/'<ReadLines>d__0'
)
IL_0000: call int32 [mscorlib]System.Environment::get_CurrentManagedThreadId()
IL_0005: ldarg.0
IL_0006: ldfld int32 DOT.Core.MiscHelpers/'<ReadLines>d__0'::'<>l__initialThreadId'
IL_000b: bne.un IL_002b
System.Environment::get_CurrentManagedThreadId()
前のメソッドからの 2 つの呼び出しが、.NET 4.5 で追加されたプロパティである に置き換えられていることに注意してください。
4.5 へのアップグレードは 4.0 C# コンパイラ (csc.exe) を上書きするため、.NET 4.0 リファレンス アセンブリがない限り、マシン上で 4.0 用にコンパイルされたコードは新しい IL テンプレートを使用し、vanilla 4.0 インストールでは実行されません [2]。これにより、コンパイラは古いバージョンの IL を生成します。
[1] つまり、それを作成したスレッド (たとえば、foreach ステートメント) で最初に消費されたときです。
[2] 実際には、.NET Framework インストーラーから .NET 4.0 コンパイラーを抽出し、それを使用してコードをコンパイルするようにプロジェクト ファイルを変更することができます。これは問題を解決する別の方法かもしれませんが、話が長くなるのでここでは詳しく説明しません