문제

C# 사양에는 전처리기 및 기본 지시문(#define, #if 등)이 포함되어 있지만 C/C++와 같은 언어에서 볼 수 있는 유연한 전처리기가 없습니다.나는 그러한 유연한 전처리기가 부족한 것이 Anders Hejlsberg가 내린 설계 결정이라고 믿습니다(불행히도 지금은 이에 대한 참조를 찾을 수 없지만).경험상 이것은 확실히 좋은 결정이었습니다. 왜냐하면 제가 C/C++를 많이 할 때 정말 끔찍하고 유지 관리가 불가능한 매크로가 생성되었기 때문입니다.

즉, 약간 더 유연한 전처리기가 유용할 수 있는 여러 가지 시나리오가 있습니다.다음과 같은 코드는 몇 가지 간단한 전처리기 지시어로 개선될 수 있습니다.

public string MyProperty
{
  get { return _myProperty; }
  set
  {
    if (value != _myProperty)
    {
      _myProperty = value;
      NotifyPropertyChanged("MyProperty");
      // This line above could be improved by replacing the literal string with
      // a pre-processor directive like "#Property", which could be translated
      // to the string value "MyProperty" This new notify call would be as follows:
      // NotifyPropertyChanged(#Property);
    }
  }
}

이처럼 매우 간단한 경우를 처리하기 위해 전처리기를 작성하는 것이 좋은 생각일까요?스티브 맥코넬은 다음과 같이 썼다. 코드 완성 (p208):

나만의 전처리기 작성 언어에 전처리기가 포함되어 있지 않으면 작성하기가 매우 쉽습니다.

나는 찢어졌다.이러한 유연한 전처리기를 C#에서 제외하는 것은 디자인상의 결정이었습니다.하지만 제가 매우 존경하는 작가는 어떤 상황에서는 괜찮을 수도 있다고 언급했습니다.

C# 전처리기를 구축해야 합니까?내가 하고 싶은 간단한 일을 할 수 있는 것이 있나요?

도움이 되었습니까?

해결책

다음과 같은 측면 지향 솔루션을 살펴보십시오. 포스트샤프, 사용자 정의 속성을 기반으로 사실 이후에 코드를 삽입합니다.이는 프리컴파일러와 반대이지만 원하는 기능(PropertyChanged 알림 등)을 제공할 수 있습니다.

다른 팁

C# 전처리기를 구축해야 합니까?내가 하고 싶은 간단한 일을 할 수 있는 것이 있나요?

언제든지 C 전처리기를 사용할 수 있습니다. C#은 구문 측면에서 충분히 가깝습니다.M4도 옵션입니다.

많은 사람들이 짧은 코드가 우아한 코드와 같다고 생각하지만 이는 사실이 아닙니다.

제안한 예는 코드에서 완벽하게 해결되었습니다. 표시된 것처럼 전처리기 지시문이 필요한 것은 무엇입니까?코드를 "전처리"하고 싶지 않고 컴파일러가 속성에 일부 코드를 삽입하기를 원합니다.일반적인 코드이지만 전처리기의 목적은 아닙니다.

당신의 예를 들어, 한계를 어디에 두나요?분명히 이는 관찰자 패턴을 만족하며 유용할 것이라는 데는 의심의 여지가 없습니다. 하지만 코드가 제공하기 때문에 실제로 수행되는 유용한 일이 많이 있습니다. 유연성 전처리기는 그렇지 않습니다.전처리기 지시문을 통해 공통 패턴을 구현하려고 하면 언어 자체만큼 강력해야 하는 전처리기가 필요하게 됩니다.당신이 원한다면 전처리기 지시문을 사용하는 등 다른 방식으로 코드를 처리합니다. 하지만 코드 조각만 원한다면 다른 방법을 찾으세요. 왜냐하면 전처리기가 그렇게 하도록 의도된 것이 아니기 때문입니다.

C++ 스타일 전처리기를 사용하면 OP 코드를 다음 한 줄로 줄일 수 있습니다.

 OBSERVABLE_PROPERTY(string, MyProperty)

OBSERVABLE_PROPERTY은 대략 다음과 같습니다.

#define OBSERVABLE_PROPERTY(propType, propName) \
private propType _##propName; \
public propType propName \
{ \
  get { return _##propName; } \
  set \
  { \
    if (value != _##propName) \
    { \
      _##propName = value; \
      NotifyPropertyChanged(#propName); \
    } \
  } \
}

처리해야 할 속성이 100개라면 코드는 약 1,200줄입니다.~100.어느 것이 더 읽기 쉽고 이해하기 쉽나요?어느 것이 쓰기 더 쉽나요?

C#을 사용하여 각 속성을 만들기 위해 잘라내어 붙여넣는다고 가정하면 속성당 8개의 붙여넣기, 즉 총 800개가 됩니다.매크로를 사용하면 붙여넣기가 전혀 되지 않습니다.코딩 오류가 포함될 가능성이 더 높은 것은 무엇입니까?예를 들어 추가해야 하는 경우 변경하기가 더 쉽습니다.IsDirty 플래그?

상당수의 경우에 사용자 정의 변형이 있을 가능성이 있는 경우 매크로는 그다지 도움이 되지 않습니다.

다른 도구와 마찬가지로 매크로도 남용될 수 있으며 잘못된 사람의 손에 들어가면 위험할 수도 있습니다.일부 프로그래머에게 이것은 종교적인 문제이며, 한 접근 방식이 다른 접근 방식보다 나은 점은 중요하지 않습니다.당신이라면 매크로를 피해야 합니다.매우 날카로운 도구를 정기적으로 능숙하고 안전하게 사용하는 사람들에게 매크로는 코딩하는 동안 즉각적인 생산성 향상을 제공할 뿐만 아니라 디버깅 및 유지 관리 중에도 다운스트림을 제공할 수 있습니다.

C#용 전처리기를 구축하는 것에 대한 주요 주장은 Visual Studio에서의 통합입니다.Intellisense와 새로운 배경 컴파일이 원활하게 작동하도록 하려면 (가능하다면) 많은 노력이 필요할 것입니다.

대안은 다음과 같은 Visual Studio 생산성 플러그인을 사용하는 것입니다. ReSharper 또는 코드러시.후자는 내가 아는 한 최고의 템플릿 시스템을 갖추고 있으며 뛰어난 기능을 제공합니다. 리팩토링 도구.

당신이 언급한 정확한 유형의 문제를 해결하는 데 도움이 될 수 있는 또 다른 것은 다음과 같은 AOP 프레임워크입니다. 포스트샤프.
그런 다음 사용자 정의 속성을 사용하여 공통 기능을 추가할 수 있습니다.

현재 실행되는 메서드의 이름을 얻으려면 스택 추적을 보면 됩니다.

public static string GetNameOfCurrentMethod()
{
    // Skip 1 frame (this method call)
    var trace = new System.Diagnostics.StackTrace( 1 );
    var frame = trace.GetFrame( 0 );
    return frame.GetMethod().Name;
}

속성 집합 메서드에 있는 경우 이름은 set_Property입니다.

동일한 기술을 사용하여 소스 파일과 줄/열 정보를 쿼리할 수도 있습니다.

그러나 나는 이것을 벤치마킹하지 않았습니다. 모든 속성 세트에 대해 stacktrace 객체를 한 번 생성하는 것은 너무 많은 시간이 소요되는 작업일 수 있습니다.

INotifyPropertyChanged를 구현할 때 문제의 중요한 부분 중 하나가 누락되었을 수 있다고 생각합니다.소비자는 부동산 이름을 결정하는 방법이 필요합니다.이러한 이유로 속성 이름을 상수나 정적 읽기 전용 문자열로 정의해야 합니다. 이렇게 하면 소비자가 속성 이름을 '추측'할 필요가 없습니다.전처리기를 사용한 경우 소비자는 속성의 문자열 이름이 무엇인지 어떻게 알 수 있습니까?

public static string MyPropertyPropertyName
public string MyProperty {
    get { return _myProperty; }
    set {
        if (!String.Equals(value, _myProperty)) {
            _myProperty = value;
            NotifyPropertyChanged(MyPropertyPropertyName);
        }
    }
}

// in the consumer.
private void MyPropertyChangedHandler(object sender,
                                      PropertyChangedEventArgs args) {
    switch (e.PropertyName) {
        case MyClass.MyPropertyPropertyName:
            // Handle property change.
            break;
    }
}

C#의 다음 버전을 디자인한다면 각 함수에 클래스 이름과 함수 이름이 포함된 로컬 변수가 자동으로 포함되는 것을 고려해 보겠습니다.대부분의 경우 컴파일러의 최적화 프로그램이 이를 제거합니다.

하지만 그런 종류의 수요가 그리 많지는 않은 것 같아요.

@Jorge는 다음과 같이 썼습니다.코드를 다른 방식으로 처리하려면 전처리기 지시문을 사용하세요. 하지만 코드 조각만 원하는 경우에는 전처리기가 그렇게 하도록 의도된 것이 아니기 때문에 다른 방법을 찾으세요.

흥미로운.나는 전처리기가 반드시 이런 식으로 작동한다고 생각하지 않습니다.제공된 예에서는 다음의 전처리기 정의와 일치하는 간단한 텍스트 대체를 수행하고 있습니다. 위키피디아.

이것이 전처리기를 적절하게 사용하지 않는 경우 일반적으로 컴파일 전에 발생해야 하는 간단한 텍스트 교체를 무엇이라고 해야 합니까?

최소한 제공된 시나리오의 경우 전처리기를 구축하는 것보다 더 깨끗하고 유형이 안전한 솔루션이 있습니다.

제네릭을 사용하십시오.다음과 같습니다:

public static class ObjectExtensions 
{
    public static string PropertyName<TModel, TProperty>( this TModel @this, Expression<Func<TModel, TProperty>> expr )
    {
        Type source = typeof(TModel);
        MemberExpression member = expr.Body as MemberExpression;

        if (member == null)
            throw new ArgumentException(String.Format(
                "Expression '{0}' refers to a method, not a property",
                expr.ToString( )));

        PropertyInfo property = member.Member as PropertyInfo;

        if (property == null)
            throw new ArgumentException(String.Format(
                "Expression '{0}' refers to a field, not a property",
                expr.ToString( )));

        if (source != property.ReflectedType ||
            !source.IsSubclassOf(property.ReflectedType) ||
            !property.ReflectedType.IsAssignableFrom(source))
            throw new ArgumentException(String.Format(
                "Expression '{0}' refers to a property that is not a member of type '{1}'.",
                expr.ToString( ),
                source));

        return property.Name;
    }
}

이는 쉽게 확장하여 다음을 반환할 수 있습니다. PropertyInfo 대신, 부동산 이름보다 훨씬 더 많은 정보를 얻을 수 있습니다.

왜냐하면 그것은 Extension method, 거의 모든 객체에 이 방법을 사용할 수 있습니다.


또한 이는 유형에 안전합니다.
충분히 강조할 수 없습니다.

(오래된 질문이라는 것을 알고 있지만 실용적인 해결책이 부족하다는 것을 알았습니다.)

C#을 버릴 준비가 되었다면 다음을 확인해 보세요. 우우 놀라울 정도로 유연한 언어 매크로 를 통해 지원 AST (추상 구문 트리) 조작.C# 언어를 버릴 수 있다면 정말 좋은 일입니다.

Boo에 대한 자세한 내용은 다음 관련 질문을 참조하세요.

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