문제

주어진 클래스에 대해 추적 기능을 갖고 싶습니다. 즉, 모든 메서드 호출 (메서드 서명 및 실제 매개 변수 값)과 모든 메서드 종료 (메서드 서명 만)를 기록하고 싶습니다.

다음을 가정하여이를 수행하는 방법 :

  • 타사를 사용하고 싶지 않습니다. C # 용 AOP 라이브러리,
  • 추적하려는 모든 메소드에 중복 코드를 추가하고 싶지 않습니다.
  • 클래스의 공용 API를 변경하고 싶지 않습니다. 클래스의 사용자는 모든 메서드를 똑같은 방식으로 호출 할 수 있어야합니다.

    질문을 더 구체적으로 만들기 위해 다음과 같은 3 가지 클래스가 있다고 가정 해 보겠습니다. 라코 디스

    Method1 Method2 에 대한 모든 호출에 대해 Logger.LogStart Logger.LogEnd 를 호출하려면 Caller.Call 메서드를 수정하고 Traced.Method1 Traced.Method2 에 대한 호출을 명시 적으로 추가하지 않습니까?

    편집 : Call 메서드를 약간 변경하면 해결 방법은 무엇입니까?

도움이 되었습니까?

해결책

C #은 AOP 지향 언어가 아닙니다. 일부 AOP 기능이 있고 다른 일부를 에뮬레이션 할 수 있지만 C #으로 AOP를 만드는 것은 고통 스럽습니다.

원하는 일을 정확히 수행 할 수있는 방법을 찾았지만 쉽게 할 수있는 방법이 없습니다.

이해 한대로 다음과 같이하세요. 라코 디스

이를 위해 두 가지 주요 옵션이 있습니다.

  1. MarshalByRefObject 또는 ContextBoundObject에서 클래스를 상속하고 IMessageSink에서 상속되는 속성을 정의합니다. 이 기사 에 좋은 예가 있습니다. 그럼에도 불구하고 MarshalByRefObject를 사용하면 성능이 지옥처럼 떨어질 것이라는 점을 고려해야합니다. 즉, 10 배 성능 손실에 대해 이야기하고 있으므로 시도하기 전에 신중하게 생각하십시오.

  2. 다른 옵션은 코드를 직접 삽입하는 것입니다. 런타임에서 모든 클래스를 "읽기"위해 리플렉션을 사용하고 해당 속성을 가져오고 적절한 호출을 주입해야합니다 (이 문제에 대해 Reflection.Emit이 생각하는 것처럼 Reflection.Emit 메서드를 사용할 수 없다고 생각합니다. 이미 존재하는 메서드 안에 새 코드를 삽입 할 수 없습니다). 디자인 타임에 이것은 CLR 컴파일러에 대한 확장을 생성하는 것을 의미하며, 이것이 어떻게 수행되는지 솔직히 알 수 없습니다.

    마지막 옵션은 IoC 프레임 워크 를 사용하는 것입니다. 대부분의 IoC 프레임 워크는 메소드를 연결할 수있는 진입 점을 정의하여 작동하므로 완벽한 솔루션이 아닐 수도 있지만 달성하려는 항목에 따라 공정한 근사치가 될 수 있습니다.

다른 팁

가장 간단한 방법은 PostSharp 를 사용하는 것입니다.적용하는 속성을 기반으로 메서드 내부에 코드를 삽입합니다.원하는 것을 정확하게 할 수 있습니다.

또 다른 옵션은 프로파일 링 API 를 사용하는 것입니다.메서드 내부에 코드를 삽입하기는하지만 정말 어렵습니다.

IDisposable 인터페이스를 구현하는 클래스를 Tracing이라고 부르면 모든 메서드 본문을 하나의 라코 디스 Tracing 클래스에서 Tracing 클래스의 생성자 / Dispose 메서드에서 각각 추적 논리를 처리하여 메서드의 시작 및 종료를 추적 할 수 있습니다.다음과 같습니다. 라코 디스

Castle Windsor 와 같은 DI 컨테이너의> 인터 셉션 기능. 실제로 특정 속성으로 장식 된 메서드가있는 모든 클래스를 가로 채도록 컨테이너를 구성 할 수 있습니다.

3 번 포인트와 관련하여 OP는 AOP 프레임 워크가없는 솔루션을 요청했습니다. 다음 답변에서 피해야 할 것은 Aspect, JointPoint, PointCut 등이라고 가정했습니다. CastleWindsor의 인터 셉션 문서 , 요청 된 작업을 수행하는 데 필요한 문서는 없습니다.

속성의 존재에 따라 인터셉터의 일반 등록 구성 : 라코 디스

생성 된 IContributeComponentModelConstruction을 컨테이너에 추가 라코 디스

그리고 인터셉터 자체에서 원하는 것은 무엇이든 할 수 있습니다 라코 디스

기록 할 방법에 로깅 속성 추가 라코 디스

클래스의 일부 메소드 만 인터셉트해야하는 경우 속성의 일부 처리가 필요합니다. 기본적으로 모든 공개 메소드가 차단됩니다.

이것 좀보세요-꽤 무거운 물건 .. http://msdn.microsoft.com/en-us/magazine/cc164165.aspx

Essential .net-don box에는 Interception이라고하는 필요한 사항에 대한 장이 있습니다. 여기에서 일부를 긁어 냈습니다 (글꼴 색상에 대해 죄송합니다-그 당시에는 어두운 테마가있었습니다 ...) http://madcoderspeak.blogspot.com/2005/09/essential-interception-using-contexts.html

더 쉬울 수있는 다른 방법을 찾았습니다 ...

메소드 InvokeMethod 선언 라코 디스

그런 다음 방법을 이렇게 정의합니다 라코 디스

이제 종속성 주입없이 런타임에 확인할 수 있습니다 ...

사이트에 문제 없음 :)

이것이 AOP 프레임 워크보다 덜 중요하거나 MarshalByRefObject에서 파생되거나 원격 또는 프록시 클래스를 사용하는 것에 동의 할 것입니다.

먼저 인터페이스를 구현하도록 클래스를 수정해야합니다 (MarshalByRefObject를 구현하는 대신). 라코 디스

다음에는 데코 레이팅 된 객체에 대한 호출을 가로 챌 수 있도록 인터페이스를 데코 레이팅하려면 RealProxy를 기반으로하는 일반 래퍼 객체가 필요합니다. 라코 디스

이제 ITraced의 Method1 및 Method2에 대한 호출을 가로 챌 준비가되었습니다. 라코 디스

제한없이 메서드를 추적하고 싶다면 (코드 적응, AOP 프레임 워크, 중복 코드 없음) 마법이 필요합니다 ...

진심으로 런타임에 작동하는 AOP 프레임 워크를 구현하도록 해결했습니다.

여기에서 찾을 수 있습니다. NConcern .NET AOP Framework

나는 이러한 종류의 요구에 대응하기 위해이 AOP 프레임 워크를 만들기로 결정했습니다. 매우 가벼운 간단한 라이브러리입니다. 홈 페이지에서 로거의 예를 볼 수 있습니다.

타사 어셈블리를 사용하지 않으려면 코드 소스 (오픈 소스)를 탐색하고 두 파일을 모두 복사 할 수 있습니다. Aspect.Directory.cs Aspect.Directory.Entry.cs 를 원하는대로 조정합니다. 이러한 클래스를 사용하면 런타임에 메서드를 대체 할 수 있습니다. 나는 당신에게 라이센스를 존중 해달라고 부탁합니다.

필요한 것을 찾거나 마침내 AOP 프레임 워크를 사용하도록 설득하기를 바랍니다.

CodePlex에서 오픈 소스 프레임 워크 CInject 를 사용할 수 있습니다.최소한의 코드를 작성하여 Injector를 만들고 CInject로 모든 코드를 빠르게 가로 챌 수 있습니다.또한 이것은 오픈 소스이기 때문에 이것을 확장 할 수도 있습니다.

또는 차단에서이 문서에 언급 된 단계를 따를 수 있습니다.IL을 사용한 메서드 호출 및 C #의 Reflection.Emit 클래스를 사용하여 자체 인터셉터를 만듭니다.

해결 방법은 모르지만 접근 방식은 다음과 같습니다.

사용자 정의 속성으로 클래스 (또는 해당 메서드)를 장식합니다.프로그램의 다른 어딘가에서 초기화 함수가 모든 유형을 반영하도록하고, 속성으로 장식 된 메소드를 읽고 메소드에 IL 코드를 삽입하십시오.실제로 LogStart, 실제 메서드 및 LogEnd를 호출하는 스텁으로 메서드를 바꾸는 더 실용적 일 수 있습니다.또한 리플렉션을 사용하여 메서드를 변경할 수 있는지 모르겠으므로 전체 유형을 대체하는 것이 더 실용적 일 수 있습니다.

잠재적으로 GOF 데코레이터 패턴을 사용하고 추적이 필요한 모든 클래스를 '장식'할 수 있습니다.

아마도 IOC 컨테이너에서만 실제로 실용적 일 수 있습니다 (하지만 앞서 언급 한 포인터로서 IOC 경로를 따라 내려 가려는 경우 메소드 인터 셉션을 고려할 수 있습니다).

Ayende가 어떻게했는지에 대한 답변을 얻기 위해 Ayende를 괴롭혀야합니다. http://ayende.com/Blog / archive / 2009 / 11 / 19 / can-you-hack-this-out.aspx

AOP는 깔끔한 코드 구현을 위해 반드시 필요하지만 C #으로 블록을 둘러싸고 싶다면 제네릭 메서드를 비교적 쉽게 사용할 수 있습니다.(인텔리 감각과 강력한 형식의 코드 포함) 확실히 AOP의 대안이 될 수는 없습니다.

PostSHarp 에는 약간의 버그 문제가 있지만 (저는프로덕션에서) 좋은 것입니다.

일반 래퍼 클래스, 라코 디스

사용법은 다음과 같을 수 있습니다 (물론 인텔리 감각으로) 라코 디스

  1. 자신의 AOP 라이브러리를 작성합니다.
  2. 반사를 사용하여 인스턴스에 로깅 프록시를 생성합니다 (기존 코드의 일부를 변경하지 않고 수행 할 수 있는지 확실하지 않음).
  3. 어셈블리를 다시 작성하고 로깅 코드를 삽입합니다 (기본적으로 1과 동일).
  4. CLR을 호스팅하고이 수준에서 로깅을 추가합니다 (CLR에 필요한 후크가 있는지 확실하지 않지만 구현하기 가장 어려운 솔루션이라고 생각합니다).

'nameof'가 출시 된 C # 6 이전에 할 수있는 최선의 방법은 느린 StackTrace 및 linq 표현식을 사용하는 것입니다.

예 :그러한 방법을 위해 라코 디스

이러한 줄이 로그 파일에 생성 될 수 있습니다. 라코 디스

구현은 다음과 같습니다. 라코 디스

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top