DTS - Everett C# IDE interface 버그

오늘은 몇일전에 한 DTS 버그에 대해 얘기 할까 한다. 여기서 DTS란 Days to Solution 이라고 고객을 통해 들어온 버그에 대해 3일 안에 해결 방법을 제시 하는걸 말한다.

하여간, 이번 버그의 요점은 C#에서 interface와 property를 실제 어떻게 구현하는가 이다. 

C# 스펙을 보면 알겠지만, C#에서 property를 구한 하는 방법은 다음과 같다.

만얀 사용자가 int prop { set; get; } 과 같은 property를 소스에서 구현하면 C# compiler가 자동으로 다음과 같은 함수를 자동으로 생성한다.

int get_prop(); void set_prop(int i);

따라서 만약 사용자가 소스에 int prop {} 과 같은 property를 구현했다면 같은 소스안에 set_prop과 get_prop 과 같은 함수를 재 정의 하는건 compiler error가 된다.

C#에서 interface를 구현 하는 방법은 다음과 같다.

만약, C# interface에서 void method(); 와 같은 함수를 정의 하고 Class A 에서 이 void method(); 를 구현 했다면, 실제 Class A의 void method의 성격은 virtual sealed가 된다.

이유는 당연히 interface에 정의된 함수를 interface를 통해 사용하자면 그 함수는 당연히 virtual table안에 정의 되어야 하니 virtual 이고, sealed인 이유는 직접적으로 virtual라는 keyword를 Class A 안의 void method에 지정해 주지 않았으니 Class A로 부터 파생된 클라스가 void method를 override 하지 못하도록 하기 위함이다.

그럼 이번 버그의 요점은 무엇이냐?

일단 밑의 코드를 보자

    public interface IFoo
    {
        int prop
        {
            get;
        }
    }

    public class CFoo : IFoo
    {

        #region IFoo Members

        public int prop
        {
            get { throw new Exception("The method or operation is not implemented."); }
            set { throw new Exception("The method or operation is not implemented."); }
        }

        #endregion
    }

만약 사용자가 다음과 같은 코드를 작성 했다면 어떻게 될까? 혹 어떤 분은 이렇게 하는게 valid 한가 어쩌 보실수도 있지만, interface를 구현 하는 class는 interface에 정의된 모든 멤버를 충족만 하면 되기 때문에 prop의 set 과 같이 interface에 없는 걸 더 지원 한다고 문제 될껀 없기 때문에 당연히 valid한 코드다.

하여간 다음과 같은 코드를 compile하면 실제 생성 되는 IL은 어떻게 될까?

위에 property와 interface에 대해 간단히 설명한거 처럼, 일단 compiler는 prop를 위해 자동으로 set_prop와 get_prop를 생성하게 된다. 근데 이때 이 두 함수의 성격은 어떻게 될까?

일단 get_prop는 interface에 정의 되어 있으므로 이 함수의 성격은 virtual sealed가 된다. 그럼 set_prop는 어떻게 될까? 이 함수는 interface에 정의 되어 있지 않고 단지 class CFoo에만 정의 되어 있으므로 아무런 성격도 같지 않는 그냥 일반 적인 get_prop가 된다.

그럼 이 모든게 무엇이 문제 인가?

문제는 Everett IDE의 버그에 있다.

일단 이 버그를 설명하기 전에 C# 의 메타 데이타가 property 정보를 어떻게 저장하는지 간략하게 알아야 한다.

사용자가 C# 소스를 컴파일 하면 흔히 사람들이 말하는 메타 데이타라는게 생성되게 된다. 이 메타 데이타 에는 그 소스에 타입에 대한 정보가 저장 되게 된다.

property의 경우 이 메타 데이타에 property 속성을 가진 prop이라는 정보가 기록 되고, 역시 같은 메타 데이타 안에 method 속성을 가진 set_prop과 get_prop이 저장 되게 된다.

그럼 IDE의 버그가 무엇이냐?

요즘 나오는 IDE라는걸 써본 사람들은 알겠지만, 요즘 나오는 IDE는 소위 말하는 intellisense라는걸 지원한다. 이게 무엇이냐면 코딩을 할때 type instance를 치고 dot을 치면 그 타입에서 외부로 공개하는 member들의 정보를 보여 준다 던지 하는것을 말한다.

그럼 이 정보를 어떻게 가져 오느냐? 이 정보를 바로 저위에서 말한 메타 데이타에서 가져 오게 된다. C++와 같이 metadata를 지원 하지 않는 언어의 경우, .H 파일을 실제 parsing 함으로써 그 정보를 얻어 오지만 c# 과 같이 메타 데이타를 지원 하는 managed code인 경우는 dll의 메타 데이타로 부터 각 타입에 대한 정보를 읽어 오게 된다.

이때 IDE가 어떻게 메타 데이타 특히 property를 읽어 오는가가 버그의 핵심이다.

기존 Everett의 경우 property를 메타데이타에서 읽어 올때, 그 프로펄티가에 대한 set_ get_ method가 있는지 확인 한다. 그리고 만약 둘다 있을 경우, 이 두 method의 성격이 같은지 확인 하고, 둘의 성격이 같을 경우만 valid한 property로 읽어 들이게 된다.

따라서 저 위의 예제를 everett에서 컴파일 한후, IDE에서 reference 하면, prop 가 property로 인식이 안되고 get_prop와 set_prop 와 같이 method로 인식 되게 된다.

그럼 이 버그가 무엇이 그리 문제 인가?

가장 큰 문제는 만약 위와 같은 코드를 사용해서 사용자가 win form component를 만들었을 때다.

C# IDE와 Winform은 서로 다른 팀이다. 따라서 두 팀간에 서로 정보를 주고 받을때 2곳에서 dll에 대한 정보가 동일 해야 한다.

근데 이 버그로 인해, C# IDE에서는 prop을 property로 인식 하지 안기 때문에 winform과 정보를 주고 받을때 이런 property를 가지고 있는 component 자체를 인식 못하게 된다.

Everett에서 winform 작업을 해보신 분들은 가끔 보셨겠지만, 어쩔때 winform을 열면 어떤 component가 사라진다 던지, 아니면 winform이 안뜨고 X 에 빨간 글씨로 에러가 뜨면서 form 자체가 안뜨는 경우가 있다던지, 아니면 셋팅이 자꾸 바뀐다 던지 하는 경우가 있다. 물론 다 이것 때문은 아니지만, 이것도 그 원인 중에 하나가 된다.

나중에 시간 나면 C# IDE와 winform이 어떻게 서로 정보를 주고 받는지에 대해서도 간략하게 쓰도록 하겠다.

하여간 이제 여태 문제를 말했으니 해결 방법을 얘기 하겠다.

물론 Everett SP1에 fix가 들어 갈테지만, 또 새로 나온 VS 2005에서는 이미 해결된 문제지만, 현 Everett에서의 workaround 방법은

virtual public int prop
        {
            get { throw new Exception("The method or operation is not implemented."); }
            set { throw new Exception("The method or operation is not implemented."); }
        }

위의 코드에서 처럼 property 를 virtual로 만드는 것이다.

왜 이게 workaround인지는 각자 생각해 보시도록 ^^ 사실 엄청 이유는 간단하다.

수고!!