VSTS 2010 팀 블로그2009.04.22 04:16

 

http://vsts2010.net/41

Visual Studio Team System 2010의 Code Analysis중 Rule Set 기능에 대해서 다룬 글을 Visual Studio Team System 2010 공식 팀 블로그에 포스팅했습니다. ^^

신고
Posted by kkongchi
VSTS 2010 팀 블로그2009.04.19 00:26
코드분석 규칙2008.01.19 11:49
이 블로그에서도 간단하게나마 FxCop Custom 규칙을 만드는 방법을 "[HowTo]FxCOP(비주얼 스튜디오 2005 코드 분석) Custom 규칙 작성하기(C#)" 라는 제목으로 소개한 바 있었다. 내용이 너무 부실해서 민망하긴 해도, 아주 간단한 규칙 정도는 작성할 수 있는 수준의 글이었다.

오늘 MS의 Code Analysis Team Blog"Tutorial on writing your own Code Analysis rule" 이라는 글이 올라와서 봤더니, BinaryCoder.Net의 Jason Kresowaty라는 친구가 올린 "FxCop: Writing Your Own Custom Rules"이라는 글을 소개하는 포스팅이었다.

아직은 대충 봤는데, Introspection Object Model에 대해서 아주 자세하게 다루는 것이 눈에 띈다. Introspector라는 자기가 만든 유틸리티도 소개를 하고 있고. 찬찬이 읽어 봐야 겠다.

아래는 그 Instrospector라는 유틸리티의 스크린샷

사용자 삽입 이미지

신고
Posted by kkongchi
규칙 클래스 이름: DoNotExposeGenericList
규칙 ID: CA1002
분류: 디자인 규칙
메시지 레벨: 에러
확실도: 95%

원인: System.Collections.Generic.List<T> 타입을 외부로 노출시켰거나, 리턴 타입으로 사용하거나, 혹은 파라미터로 사용했을 때 이 규칙에 위반된다.

규칙 설명
System.Collections.Generic.List<T>는 성능을 위해서 디자인된 제네릭 컬렉션이며 상속성등은 고려되지 않았다. 그래서 어떤 가상 멤버도 그 안에 포함할 수가 없다. 아래의 제네릭 컬렉션은 상속성을 고려해서 디자인되었으므로, 리턴 타입/파라미터/외부 노출 형식에는 아래 타입들을 써야 할 것이다.

원문: http://www.gotdotnet.com/team/fxcop/Docs/Rules/Design/DoNotExposeGenericLists.html

* System.Collections.Generic.List<T>는 MSDN을 보면 나와있듯이 ArrayList의 Generic 버전이라고 생각하면 된다. 즉, 동적으로 크기가 늘어나고 빠르게 접근할 수 있는 특정 구현 형태로 보면 될 것이다.
외부 노출/리턴 타입/파라미터 등은 각 클래스들이 관계를 맺는 방식에 영향을 줄 수 있다. 이런 곳에서 상속할 수 없는 특정 구현 형태를 사용함으로써 그것에 얽매이는 것은 매우 좋지 않다는 것이 바로 이 규칙이 말하는 바라고 생각된다.
신고
Posted by kkongchi
규칙클래스이름:TypesThatOwnDisposableFieldsShouldBeDisposable
규칙ID:CA1001
분류:디자인 규칙
메시지 레벨:심각한 에러
확실도:95%

원인: System.IDisposable 인터페이스를 구현한 인스턴스 필드를 선언한 클래스에서 IDisposable 인터페이스를 구현하지 않은 경우에 해당한다.

규칙설명
클래스는 가지고 있는 관리되지 않는 리소스(파일 스트림 등의)를 제거하기 위해서 IDisposable 인터페이스를 구현하게 된다. IDisposable 형식의 인스턴스 필드가 있다는 것이 바로 그 필드가 관리되지 않는 리소스를 가지고 있다는 것을 말해 주는 것이다. 간접적으로 관리되지 않는 리소스를 가지고 있는 IDisposable 필드를 선언한 클래스는 반드시 IDisposable 인터페이스를 구현해야 한다. 또 주의해야 할 것은 직접적으로 어떤 관리되지 않는 리소스를 가지고 있지 않는 클래스는 finalizer를 구현해서는 안된다.

예제코드

[C#]
using System;
using System.IO;
 
namespace DesignLibrary
{
   //규칙을 위반한 예제이다.
   public class NoDisposeMethod
   {
      FileStream newFile;

      public NoDisposeMethod()
      {
         newFile = new FileStream(@"c:\temp.txt", FileMode.Open);
      }
   }

   //이 클래스 구현은 규칙을 만족한다.
   public class HasDisposeMethod: IDisposable
   {
      FileStream newFile;

      public HasDisposeMethod()
      {
         newFile = new FileStream(@"c:\temp.txt", FileMode.Open);
      }

      public void Dispose()
      {
         newFile.Close();
      }
   }
}


[VB.NET]
Imports System
Imports System.IO
 
Namespace DesignLibrary

  '규칙을 위반한 예제이다
   Public Class NoDisposeMethod
   
      Dim newFile As FileStream

      Sub New()
         newFile = New FileStream("c:\temp.txt", FileMode.Open)
      End Sub

   End Class

  '규칙을 만족하는 구현이다.
   Public Class HasDisposeMethod
      Implements IDisposable
   
      Dim newFile As FileStream

      Sub New()
         newFile = New FileStream("c:\temp.txt", FileMode.Open)
      End Sub

      Sub Dispose() Implements IDisposable.Dispose
         newFile.Close()
      End Sub

   End Class

End Namespace


관련 규칙
삭제가능한 필드는 삭제되어야 한다.
삭제가능한 형식은 finalizer를 선언해야 한다.
Dispose 메서드는 베이스 클래스의 Dispose 메서드를 호출해야 한다.
native 리소스를 가지고 있는 형식은 삭제가능해야 한다.


원문주소: http://www.gotdotnet.com/team/fxcop/Docs/Rules/Design/TypesThatOwnDisposableFieldsShouldBeDisposable.html
신고
Posted by kkongchi

규칙 클래스 이름: DoNotDeclareStaticMembersOnGenericTypes
규칙 ID: CA1000
분류: 디자인 규칙
메시지 레벨: 에러
확실도: 95% (확실도는 코드 분석에서 이 규칙에 위반 사례를 찾는 것에 대한 것이다. 즉, 이 규칙에 위반되었다는 것에 대해서 95%의 확실도로 보증할 수 있다는 것이다)

원인: 외부에 노출된 제네릭 타입이 정적(static, vb.net에서는 shared) 멤버를 가질 때 이 규칙에 위반된다.

규칙 설명
제네릭 타입의 정적 멤버를 호출할 때는 형식 매개 변수를 반드시 명시되어야 한다. Inference(유추 - 컴파일러가 인자를 통해서 형식 인자를 자동으로 알아내는 것을 말한다)를 지원하지 않는 제네릭 인스턴스 멤버를 호출할 때도, 형식 매개 변수의 형식이 반드시 명시되어야 한다. 이 두 케이스의 형식 매개 변수를 지정하는 구문은 다르지만, 아래 예에서 보듯이 혼동되기 쉽다.

[C#]

// Static method in a generic t.
GenericType<int>.StaticMethod();

// Generic instance method that does not support inference.
someObject.GenericMethod<int>();

[VB]
'Shared method in a generic type.
GenericType(Of Integer).SharedMethod()

'Generic instance method that does not support inference.
someObject.GenericMethod(Of Integer)()

위의 두 가지 코딩 방법은 멤버가 호출될 때 형식 매개 변수를 특정한 형식으로 정하지 않기 위해서 피해야 한다. 위와 같은 코딩을 하면 결과적으로 제네릭 타입을 쓰지 않은 것과 차이가 없다. 이 위반 사항을 고치기 위해서는, 정적 멤버를 없애고 그것을 인스턴스 멤버로 바꾸어야 한다.

관련 규칙
제네릭 타입에 과도한 파라미터를 피한다.
컬렉션 타입은 제네릭 인터페이스를 구현해야 한다
제네릭 리스트를 public으로 노출하지 않는다
제네릭 타입을 중첩하지 않는다

원문: http://www.gotdotnet.com/team/fxcop/Docs/Rules/Design/DoNotDeclareStaticMembersOnGenericTypes.html

신고
Posted by kkongchi
디자인 규칙은 클래스 라이브러리 디자인 가이드라인에 따르는 지를 검사하는 규칙들의 모음이다. 닷넷의 기본적인 사상에 부합되는 클래스, 인터페이스, 예외 처리 등의 디자인이 되었는지(예:CA1020 형식이 부족한 네임스페이스를 사용하지 마십시오), .NET Class Library에서 적용된 기본적인 설계 원칙들을 따르고 있는 지(예: CA1056 Uri 속성은 문자열이면 안 됩니다), .NET Framework의 기본적인 규칙을 따로 있는지(예:CA2210 어셈블리에는 올바른 강력한 이름을 사용해야 합니다) 등을 검사하는 것이라고 할 수 있다.

디자인 규칙에는 아래와 같은 규칙들이 있다.

CA1000 정적 멤버를 제네릭 형식으로 선언하지 마십시오
CA1001 삭제 가능한 필드가 있는 형식은 삭제 가능해야 합니다.
CA1002 제네릭 목록을 노출하지 마십시오.
CA1003 제네릭 이벤트 처리기 인스턴스를 사용하십시오.
(추가 예정... 각 규칙을 클릭하면 규칙에 대한 포스트로 이동한다)
신고
Posted by kkongchi
.NET General2006.07.25 23:40

FxCop은 MS에서 만든 코드 분석 툴이다. 닷넷 코드를 분석해서 표준적인 규칙에 맞는지, 성능에 문제가 없는지 등을 검사해주는 도구로, 많은 프로젝트에서 코드 리뷰를 위한 툴로 많이 사용되고 있다.


이번 Visual Studio Team System에서는, 이 FxCop이 아예 Visual Studio 내부에 내장되어서, 아주 간단하게 코드 분석을 해볼 수 있게 되었다. 솔루션 탐색기에서 프로젝트를 오른쪽 클릭해서 나오는 컨텍스트 메뉴에서 "코드 분석 실행"이라는 것을 클릭하면 바로 FxCop을 이용한 코드 리뷰를 실행하고 그 결과를 얻을 수 있다.


솔루션 탐색기에서 코드 리뷰 실행




코드 리뷰 결과 화면




이 코드 분석 툴에는 200개 정도의 기본 규칙들이 있다. 디자인, 명명, 보안, 사용, 상호 운용성, 성능, 안정성, 유지 관리, 이동성, 이식성, 전역화 등으로 분류된 이런 규칙들은 MS에서 제시하는 닷넷의 매우 표준적인 규칙에 의거해서 제작되었기 때문에 이 규칙들을 적용해서 코드를 분석해보고 규칙에 따르도록 코드를 고치는 것은 전체적인 코드의 질을 높이고 결과물의 퀄리티와 성능, 유지 관리성 등을 높이는데 매우 도움이 된다.


그런데, 실제 SI 프로젝트에서는 아무래도 Custom Rule이 필요하다. 많은 사람들이 참여하는 프로젝트에서 이런 자동화된 툴을 사용해서 개발 표준 준수여부를 분석하는 것은 시간과 리소스의 절약을 가져오기 때문에, 그 프로젝트에만 있는 규칙에 대한 준수 여부를 체크할 수 있는 규칙을 만들어서 사용한다면 아주 유용하다.


그리고, 이 FxCop은 Custom Rule을 작성할 수 있는 구조를 지원한다. FxCop의 규칙은 닷넷 어셈블리(DLL) 형태로 만들어져 있는데, 이 DLL을 만들 수 있도록 FxCopSdk.dll, Microsoft.Cci.dll 등의 참조할 수 있는 어셈블리를 제공하고 있다. 이 FxCop은 코드를 분석하는데 reflection을 쓰지 않고 introspection이라는 것을 사용한다. 이 introspection은 reflection과 같이 MSIL 코드를 분석해서 어셈블리(내부의 클래스 등의 모든 정보 또한)의 정보를 추출하는데, reflection보다 더 빠르다.


Custom Rule을 만드는 절차는 다음과 같다.


1. Visual Studio 2005에서 클래스 라이브러리 프로젝트를 만든다.


2. FxCopSdk.dll 과 Microsoft.Cci.dll을 참조해야 한다. 이 DLL들은 비주얼 스튜디오가 설치된 프로젝트 아래에 \Team Tools\Static Analysis Tools\FxCop 폴더에 있다.


3. 먼저 BaseRule 클래스를 만들어야 한다. 이 Base 클래스는 현재 만드는 프로젝트 내부의 모든 Rule 클래스들이 상속하게 될 클래스로, 리소스 XML파일을 로딩하는 역할을 한다. 이 Base 클래스는 Microsoft.FxCop.Sdk.Introspection.BaseIntrospectionRule 클래스를 상속해서 만들어야 하고, 아래의 샘플 코드와 똑같이 만들면 된다. (물론 두번째 파라미터의 XML 리소스 이름, 세번째 파라미터 내부의 클래스 자신의 이름은 바꿔주어야 한다)


using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.FxCop.Sdk.Introspection;
using Microsoft.FxCop.Sdk;
using Microsoft.Cci;


namespace FxCopSampleRule
{
// 반드시 BaseIntrospectionRule 클래스를 상속해야 한다.
public abstract class BaseFxCopSampleRule : Microsoft.FxCop.Sdk.Introspection.BaseIntrospectionRule
{
protected BaseFxCopSampleRule(string ruleIdentifier)
  : base(ruleIdentifier, "FxCopSampleRule.FxCopSampleRule", typeof(BaseFxCopSampleRule).Assembly)
{
}
}
}


4. 리소스 XML 파일을 만든다. 이 리소스 파일에는 이 어셈블리의 규칙 클래스들이 사용하게 될 메시지(규칙을 어긴 문제에 대한 이름, 설명, 해결책 등)들이 들어가게 된다. 만들 때는 리소스 파일이 아니라 XML 파일로 만들어야 하며, 중요한 것은 이 XML 파일의 속성에서 반드시 빌드 작업을 "포함 리소스"로 해야 한다는 것.




<Rules FriendlyName="AARule">
<Rule TypeName="ShouldHaveAANameSpace" Category="AA.Naming" CheckId="AA0001">
<Name>Should Have AA NameSpace</Name>
<Description>Should Have SK NameSpace</Description>
<Url></Url>
<Resolution Name="Namespace">네임스페이스는 반드시 AA.BB로 시작해야 합니다. {0} 네임스페이스를 고치세요</Resolution>
<Email>kkongchi@interdev.co.kr</Email>
<MessageLevel Certainty="95">Error</MessageLevel>
<FixCategories>Breaking</FixCategories>
<Owner>kkongchi</Owner>
<GroupOwner>AA</GroupOwner>
<DevOwner>kkongchi</DevOwner>
</Rule>
</Rules>


5. 이제 규칙을 만들 차례이다. 규칙은 하나의 클래스로 만드는데, 3번에서 만든 BaseRule 클래스를 상속해서 만든다. 이름은 규칙을 잘 나타내도록 만드시기 바란다. 생성자에서 Base 생성자를 상속할 때에 현재 만드는 규칙이 사용할 메시지의 RuleName을 파라미터로 넣어서 그 메시지를 로드할 수 있도록 해야 한다.


6. 그리고 그 규칙 클래스에서는 Check 메소드를 오버라이드해서 체크할 규칙을 구현하면 된다. Check 메서드에는 많은 오버로드된 버전이 있기 때문에 만들려고 하는 규칙에 맞는 것을 선택해야 한다. 아래의 샘플은 어셈블리의 네임스페이스를 체크하는 것이기 때문에 Module이 파라미터인 것을 선택했다. 체크해야 할 규칙에 어긋나는 케이스가 발생했을 때, Problems 프로퍼티에 새로운 Problem 객체를 Add시키면 된다. 그리고 그 Problem 객체에는 XML에서 로드하는 Resolution 객체가 필요하다. (코드를 보면 알겠지만, {0} 등의 형태로 메시지 내부의 문자열에 현재 검사한 모듈의 이름 등을 넣을 수가 있도록 되어 있다.


using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.FxCop.Sdk.Introspection;
using Microsoft.FxCop.Sdk;
using Microsoft.Cci;

namespace FxCopSampleRule
{
// 먼저 만든 Base 클래스를 상속한다.
public class ShouldHaveAANameSpace : BaseFxCopSampleRule
{
//리소스 XML파일에 있는 규칙들 중에서 ShouldHaveAANameSpace (이름)이 반드시 있어야 한다.
public ShouldHaveAANameSpace() : base("ShouldHaveAANameSpace")
{
}

/// <summary>
/// 네임스페이스가 AA.BB로 시작하는지를 체크하는 규칙이다.
/// </summary>
/// <param name="module"></param>
/// <returns></returns>
public override ProblemCollection Check(Module module)
{
for (int i = 0; i < module.GetNamespaceList().Length; i++)
{
  if (!module.GetNamespaceList()[i].FullName.StartsWith("AA.BB"))
  {
   //Problem 객체를 만들어서 base.Problems에 더해야 한다.
   //GetResolution 함수의 파라미터는 XML에 정의된 Resolution에서 {0}, {1} 로 표시된 문자열을 채우는 값이다.
   base.Problems.Add(new Problem(GetResolution(new string[1] { module.GetNamespaceList()[i].FullName })));
  }
}

return base.Problems;
}
}
}


7. 코드가 완성되면 컴파일을 하면 된다. 컴파일된 DLL을 비주얼 스튜디오가 설치된 디렉토리 아래의 Team Tools\Static Analysis Tools\FxCop\Rules 폴더에 복사하면, 바로 그 후부터 코드 분석 시에 새로 만든 규칙을 사용할 수가 있다.


신고
Posted by kkongchi

티스토리 툴바