マイクロソフトは循環参照を含むアセンブリをどのように作成しましたか?


107

.NET BCLでは、以下の間に循環参照があります。

  • System.dll そして System.Xml.dll
  • System.dll そして System.Configuration.dll
  • System.Xml.dll そして System.Configuration.dll

これが.NET Reflectorのスクリーンショットで、私が何を意味するかを示しています。

ここに画像の説明を入力してください

マイクロソフトがこれらのアセンブリを作成した方法は私には謎です。これを可能にするために特別なコンパイルプロセスが必要ですか?ここで何か面白いことが起こっていると思います。


2
非常に良い質問です。私は実際にこれを検査するために時間をかけたことはありませんが、答えを知りたいと思っています。実際、Dykamが賢明なものを提供してくれたようです。
ノルドリン2009

3
それらすべてが互いに必要な場合、それらのdllが1つにマージされないのはなぜですか?そのための実用的な理由はありますか?
アンドレアスピーターソン

1
興味深い質問です。これに対するエリックリッペルトの答えを知りたいのですが。アンドレアスが言ったように、私は...彼らは同じアセンブリ内のすべてのものを入れていない理由を不思議に思う
トーマス・レベスクに

1つのアセンブリを更新する必要がある場合でも、他のアセンブリに触れる必要はありません。それが私が見る唯一の理由です。しかし興味深い質問
Atmocreations 2009

2
このプレゼンテーション(asmmetaファイル)をご覧ください。msakademik.net
Mehrdad Afshari

回答:


58

Monoプロジェクトがこれをどのように行うかしかわかりません。定理は非常に単純ですが、コードは混乱します。

最初にSystem.Configuration.dllをコンパイルしますが、一部はSystem.Xml.dllへの参照を必要としません。この後、通常の方法でSystem.Xml.dllをコンパイルします。今魔法が来ます。それらはSystem.configuration.dllを再コンパイルしますが、一部はSystem.Xml.dllへの参照を必要とします。これで、循環参照によるコンパイルが成功しました。

要するに:

  • Aは、BとBへの参照を必要とするコードなしでコンパイルされます。
  • Bがコンパイルされます。
  • Aが再コンパイルされます。

1
これはVisual Studioによってブロックされますが、コマンドラインコンパイラ(csc.exe)を直接使用して実行できます。私の答えを見てください。
アルフレッドマイヤーズ

14
知っている。MonoのメインビルドシステムはVisual Studioではありません。マイクロソフトもそうではないと思います。
Dykam 2009

35

RBarryYoungとDykamは何かに夢中になっています。マイクロソフトは、ILDASMを使用してアセンブリを分解し、すべての内部/プライベートなものとメソッド本体を取り除き、ILを(ILASMを使用して)再コンパイルして、いわゆる「脱水されたアセンブリ」またはメタデータアセンブリに再コンパイルします。これは、アセンブリのパブリックインターフェイスが変更されるたびに行われます。

ビルド時には、実際のアセンブリではなくメタデータアセンブリが使用されます。そのようにサイクルが壊れています。


1
興味深い答えです。リンクはありますか?
ヘンクホルターマン、

ツールへの外部参照を見つけようとしています。私はそれがマイクロソフトの外で公開されているとは思いませんが、概念は単純です:disassemble-strip internals-reassemble。
Srdjan Jovcic 2009

同意-興味深い答え。これをバックアップするいくつかのリンクが良いでしょう。
Drew Noakes、

はい、それは確かにそれが行われている方法です(個人的な経験から)。
Pavel Minaev、2009年

1
これらはビルド後(それらは遅延署名されている)まで強く署名されないため、脱水されたアセンブリは署名されません。
Srdjan Jovcic 2009年

26

Dykamが説明した方法で実行できますが、Visual Studioはそれをブロックします。

コマンドラインコンパイラcsc.exeを直接使用する必要があります。

  1. csc / target:library ClassA.cs

  2. csc / target:library ClassB.cs /reference:ClassA.dll

  3. csc / target:library ClassA.cs ClassC.cs /reference:ClassB.dll


//ClassA.cs
namespace CircularA {
    public class ClassA {
    }
}


//ClassB.cs
using CircularA;
namespace CircularB {
    public class ClassB : ClassA  {
    }
}


//ClassC.cs
namespace CircularA {
    class ClassC : ClassB {
    }
}

Visual Studioでもこれを行うことができますが、基本的には#ifを使用し、ソリューションエクスプローラーを使用して参照を削除し、3番目の手順で逆にします。私が考えている別の方法は、同じファイルを含むが異なる参照を含む3番目のプロジェクトファイルです。これは、ビルド順序を指定できるので機能します。
Dykam 2009

私の知る限り、ここではテストできません。
Dykam 2009

本当に見てみたいです。ここで実験したことから、参照を追加しようとした瞬間に、IDEが停止します。
アルフレッドマイヤーズ

知っている。しかし、その参照と#ifシンボルを持たない3番目のプロジェクトは、最初のプロジェクトによって参照される2番目のプロジェクトによって参照されます。サイクルなし。しかし、3番目は最初のコードを使用し、最初のアセンブリの場所に出力します。アセンブリは、同じ仕様の別のアセンブリと簡単に交換できます。しかし、強い命名はこの方法で問題を引き起こす可能性があると思います。
Dykam 2009

方法は異なりますが、Srdjanの答えに少し似ています。
Dykam 2009

18

プロジェクト参照を使用しない限り、Visual Studioで実行するのはかなり簡単です...これを試してください:

  1. オープンビジュアルスタジオ
  2. 2つのクラスライブラリプロジェクト「ClassLibrary1」と「ClassLibrary2」を作成します。
  3. ビルド
  4. ClassLibrary1から、手順3で作成したdllを参照してClassLibrary2への参照を追加します。
  5. ClassLibrary2から、手順3で作成したdllを参照してClassLibrary1への参照を追加します。
  6. 再度ビルド(注:両方のプロジェクトに変更を加える場合、両方の参照を「新鮮」にするために2回ビルドする必要があります)

これがあなたのやり方です。でもまじめに...実際のプロジェクトでは絶対にやらないでください!もしそうなら、サンタは今年あな​​たにプレゼントを持っていません。


1
唯一の例外は、12月
26

6

非循環のアセンブリのセットから始めて、ILMergeを使用して、より小さなアセンブリを論理的に関連するグループに結合することで、それができると思います。


4

まあ、私はWindowsでこれを行ったことはありませんが、実際の先祖として機能した多くのcompile-link-rtl環境で実行しました。まず、相互参照なしでスタブを「ターゲット」にしてからリンクし、次に循環参照を追加してから、再リンクします。リンカは通常、循環参照または後続の参照チェーンを気にせず、それ自体で各参照を解決できることだけを気にします。

したがって、相互に参照する必要がある2つのライブラリAとBがある場合は、次のようにしてください。

  1. Bへの参照なしでAをリンクします。
  2. リンクB Aにレフリー
  3. リンクA、Bへの参照を追加。

Dykamは良い点を作ります、それはコンパイルされますが、.Netでリンクされませんが、原則は同じです:相互参照されたソースを作成し、それらのエクスポートされたエントリーポイントを使用しますが、他のすべてへの独自の参照をスタブしますでる。そのように構築します。次に、外部参照のスタブを解除して再構築します。これは特別なツールがなくても機能するはずです。実際、このアプローチは私が試したすべてのオペレーティングシステムで機能しました(そのうちの約6つ)。それを自動化するものは明らかに大きな助けになるでしょうが。


定理は正しい。ただし、.Netの世界では、リンクは動的に行われ、問題はありません。このソリューションが必要なのは、コンパイルのステップです。
Dykam 2009

もう一度修正して申し訳ありません:P。しかし、コンパイル時の参照(リンク)は.Netの世界で発生します。これは、その特定のECMA仕様から派生したすべてです。したがって、Mono、dotGnu、.Netです。Windows自体ではありません。
Dykam 2009

1

1つの可能なアプローチは、条件付きコンパイル(#if)を使用して、最初に他のアセンブリに依存しないSystem.dllをコンパイルし、次に他のアセンブリをコンパイルし、最後にSystem.dllを再コンパイルして、Xmlと構成。


1
残念ながら、これでは条件付きでアセンブリを参照することはできません(それが可能だったらいいのですが、私のプロジェクトの1つで本当に役に立ちます...)
Thomas Levesque

1
.csprojファイルを編集することで、条件付き参照を簡単に行うことができます。<Reference>要素にCondition属性を追加するだけです。
ダニエル

0

技術的には、これらがまったくコンパイルされておらず、手動で組み立てられた可能性があります。結局のところ、これらは低レベルのライブラリです。


あんまり。低レベルのものは多くありません。基本的なものだけです。低レベルになると思ったのはなぜですか?ランタイムとcorlibは低レベルです。比較的。まだプレーンなCまたはC ++ですが、JITには低レベルのものが含まれていると考えました。
Dykam、2009年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.