Microsoft는 순환 참조가있는 어셈블리를 어떻게 만들었습니까?


107

.NET BCL에는 다음 사이에 순환 참조가 있습니다.

  • System.dllSystem.Xml.dll
  • System.dllSystem.Configuration.dll
  • System.Xml.dllSystem.Configuration.dll

다음은 내가 의미하는 바를 보여주는 .NET Reflector의 스크린 샷입니다.

여기에 이미지 설명 입력

Microsoft가 이러한 어셈블리를 만든 방법은 나에게 미스터리입니다. 이를 허용하려면 특별한 컴파일 프로세스가 필요합니까? 여기서 흥미로운 일이 벌어지고 있다고 생각합니다.


2
아주 좋은 질문입니다. 나는 실제로 이것을 조사하는 데 시간을 들이지 않았지만 답을 알고 싶습니다. 사실, Dykam이 합리적인 것을 제공 한 것 같습니다.
Noldorin

3
모두 서로를 필요로한다면 왜 그 dll이 하나로 병합되지 않습니까? 그에 대한 실질적인 이유가 있습니까?
Andreas Petersson

1
흥미로운 질문 ... 나는 이것에 대한 Eric Lippert의 대답을 알고 싶습니다! Andreas가 말했듯이, 왜 그들이 모든 것을 같은 어셈블리에 넣지 않았는지 궁금합니다 ...
Thomas Levesque

한 어셈블리를 업데이트해야하는 경우 다른 어셈블리를 건드릴 필요가 없습니다. 그게 내가 보는 유일한 이유입니다. 그래도 흥미로운 질문
Atmocreations 2009-08-23

2
이 프레젠테이션 (asmmeta 파일)을 살펴보십시오. msakademik.net/academicdays2005/Serge_Lidin.ppt
Mehrdad Afshari

답변:


58

Mono Project가 어떻게이 작업을 수행하는지 알 수 있습니다. 정리는 매우 간단하지만 코드가 엉망입니다.

먼저 System.Xml.dll에 대한 참조가 필요한 부분없이 System.Configuration.dll을 컴파일합니다. 그런 다음 일반적인 방식으로 System.Xml.dll을 컴파일합니다. 이제 마법이 온다. System.Xml.dll에 대한 참조가 필요한 부분으로 System.configuration.dll을 다시 컴파일합니다. 이제 순환 참조를 사용한 성공적인 컴파일이 있습니다.

요컨대 :

  • A는 B가 필요한 코드와 B에 대한 참조없이 컴파일됩니다.
  • B가 컴파일됩니다.
  • A가 다시 컴파일됩니다.

1
Visual Studio에 의해 차단되지만 명령 줄 컴파일러 (csc.exe)를 직접 사용하여 수행 할 수 있습니다. 내 대답을 참조하십시오.
Alfred Myers

14
알아. Mono의 기본 빌드 시스템은 Visual Studio가 아닙니다. 마이크로 소프트도 그렇지 않다고 생각한다.
Dykam

35

RBarryYoung과 Dykam이 뭔가에 있습니다. Microsoft는 ILDASM을 사용하여 어셈블리를 분해하고 모든 내부 / 개인 항목 및 메서드 본문을 제거하고 IL을 다시 컴파일 (ILSM 사용)하여 '탈수 어셈블리'또는 메타 데이터 어셈블리로 다시 컴파일하는 내부 도구를 사용합니다. 이것은 어셈블리의 공용 인터페이스가 변경 될 때마다 수행됩니다.

빌드하는 동안 실제 어셈블리 대신 메타 데이터 어셈블리가 사용됩니다. 그렇게하면 사이클이 깨집니다.


1
흥미로운 답변입니다. 링크가 있습니까?
Henk Holterman

도구에 대한 외부 참조를 찾으려고합니다. 나는 그것이 마이크로 소프트 외부에서 출판되었다고 생각하지 않지만 개념은 간단하다 : 분해-스트립 내부-재 조립.
Srdjan Jovcic

동의 함-흥미로운 대답. 이를 뒷받침하는 몇 가지 링크가 좋습니다.
Drew Noakes

예, 그것이 실제로 수행되는 방식입니다 (개인 경험에서).
Pavel Minaev

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를 사용하고 솔루션 탐색기를 사용하여 참조를 제거하고 세 번째 단계에서 반대로하는 것입니다. 내가 생각하는 다른 방법은 동일한 파일이지만 다른 참조를 포함하는 세 번째 프로젝트 파일입니다. 이것은 빌드 순서를 지정할 수 있으므로 작동합니다.
Dykam

내가 아는 한 여기서 테스트 할 수 없습니다.
Dykam

정말보고 싶습니다. 여기에서 실험 한 내용에서 참조 추가를 시도하는 순간 IDE가 중단됩니다.
Alfred Myers

알아. 그러나 해당 참조와 #if 기호가없는 세 번째 프로젝트는 첫 번째가 참조하는 두 번째 프로젝트에서 참조됩니다. 사이클이 없습니다. 그러나 세 번째는 첫 번째 코드를 사용하고 첫 번째 어셈블리 위치로 출력합니다. 어셈블리는 동일한 사양의 다른 어셈블리로 쉽게 교체 할 수 있습니다. 하지만 강력한 이름이이 방법에서 문제를 일으킬 수 있다고 생각합니다.
Dykam

다른 방법이지만 Srdjan의 대답과 약간 비슷합니다.
Dykam

18

프로젝트 참조를 사용하지 않는 한 Visual Studio에서 매우 쉽게 수행 할 수 있습니다. 다음을 시도해보십시오.

  1. Visual Studio 열기
  2. 2 개의 클래스 라이브러리 프로젝트 "ClassLibrary1"및 "ClassLibrary2"를 만듭니다.
  3. 짓다
  4. ClassLibrary1에서 3 단계에서 만든 dll을 찾아 ClassLibrary2에 대한 참조를 추가합니다.
  5. ClassLibrary2에서 3 단계에서 만든 dll을 찾아 ClassLibrary1에 대한 참조를 추가합니다.
  6. 다시 빌드 (참고 : 두 프로젝트 모두에서 변경 한 경우 두 참조를 "새로"만들기 위해 두 번 빌드해야 함)

그래서 이것이 당신이하는 방법입니다. 하지만 진지하게 ... 실제 프로젝트에서 절대하지 마세요! 그렇게한다면, 산타는 올해 어떤 선물도주지 않을 것입니다.


1
유일한 예외는 12 월 26-31 일 사이이고 선물이 이미 확보 된 경우입니다
Jesse Hufstetler

6

비순환 어셈블리 집합으로 시작하고 ILMerge를 사용하여 더 작은 어셈블리를 논리적으로 관련된 그룹으로 통합하여 수행 할 수 있다고 생각합니다.


4

글쎄, 나는 그것을 Windows에서 한 적이 없지만 실용적인 조상 역할을 한 많은 compile-link-rtl 환경에서 해왔습니다. 먼저 상호 참조없이 스텁 "대상"을 만든 다음 연결 한 다음 순환 참조를 추가 한 다음 다시 연결합니다. 링커는 일반적으로 순환 참조 또는 후속 참조 체인에 대해 신경 쓰지 않고 각 참조를 자체적으로 해결할 수 있는지에 만 관심이 있습니다.

따라서 서로를 참조해야하는 두 개의 라이브러리 A와 B가있는 경우 다음과 같이 시도하십시오.

  1. B에 대한 참조없이 A를 연결합니다.
  2. B 를 A에 대한 참조 연결 합니다.
  3. 링크 A, 참조를 B에 추가합니다.

Dykam은 좋은 지적을합니다. .Net에서 링크가 아닌 컴파일이지만 원칙은 동일합니다. 내 보낸 진입 점을 사용하여 상호 참조 된 소스를 만듭니다. 밖. 그렇게 만드십시오. 그런 다음 외부 참조의 스텁을 풀고 다시 빌드하십시오. 이것은 특별한 도구 없이도 작동 할 것입니다. 사실이 접근 방식은 제가 사용해 본 모든 운영 체제에서 작동했습니다 (그 중 약 6 개). 분명히 자동화하는 것이 큰 도움이 될 것입니다.


정리가 맞습니다. 그러나 .Net 세계에서는 연결이 문제가 아닌 동적으로 수행됩니다. 이 솔루션이 필요한 컴파일 단계입니다.
Dykam

다시 고쳐서 죄송합니다 : P. 그러나 컴파일 타임에 참조 (링크)는 .Net 세계에서 발생하며, 이는 특정 ECMA 사양에서 파생 된 모든 것입니다. 따라서 Mono, dotGnu 및 .Net. Windows 자체가 아닙니다.
Dykam

1

한 가지 가능한 접근 방식은 조건부 컴파일 (#if)을 사용하여 다른 어셈블리에 종속되지 않는 System.dll을 먼저 컴파일 한 다음 다른 어셈블리를 컴파일 한 다음 마지막으로 System.dll을 다시 컴파일하여 Xml 및 구성.


1
불행히도 이것은 어셈블리를 조건부로 참조하는 것을 허용하지 않습니다 (가능했으면 좋겠어요. 제 프로젝트 중 하나에서 정말 도움이 될 것입니다 ...)
Thomas Levesque

1
.csproj 파일을 편집하여 조건부 참조를 쉽게 수행 할 수 있습니다. <Reference> 요소에 Condition 속성을 추가하기 만하면됩니다.
Daniel

0

기술적으로는 이것들이 전혀 컴파일되지 않고 손으로 조립되지 않았을 수 있습니다. 결국 이것들은 저수준 라이브러리입니다.


별로. 저수준의 물건은 많지 않고 기본 만 있습니다. 낮은 수준 일 것이라고 생각한 이유는 무엇입니까? 런타임 및 corlib는 낮은 수준입니다. 상대적으로. 여전히 평범한 C 또는 C ++, JIT에 저수준 항목이 포함되어 있다고 생각했습니다.
Dykam
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.