2014-09-23 2 views
4

This page없이 클래스 속성에 기반 인터페이스를 구현에는 다음 티저 : 발생할 것입니다 일반적인 상황PostSharp 웹 사이트에서 반사

하나는 많은 클래스에서 특정 인터페이스를 구현 할 필요가 . INotifyPropertyChanged, IDispose, IEquatable 또는 사용자가 만든 사용자 지정 인터페이스 일 수 있습니다.

나는 그것이 (바람직하게는 컴파일 시간에 대신 런타임에 반사를 사용하여)에 적용된 클래스의 속성을 기반으로 IEquatable의 일반적인 버전을 구현하는 사용자 정의 측면을 쓰고 싶습니다

. 매번 사용자 지정 메서드를 구현하는 대신 간단한 클래스에 특성을 추가하는 것이 좋습니다. 그게 가능하니? 이 소개에서 특별히 언급 되었기 때문에 나는 희망하지만, 예제 코드를 추적 할 수는 없었다.

나는 IIdentifiable 인터페이스를 소개하는 예제가 포함 된 PostSharp 웹 사이트에서 this example을 보았습니다. 그러나 새로운 인터페이스가 추가 된 클래스와 독립적 인 GUID을 반환합니다.

적용된 유형의 속성에 따라 IEquatable을 구현하는 맞춤 속성을 구성하는 방법이 있습니까 (즉, 모든 속성이 동일하면 두 인스턴스를 동일하게 만듭니다).

나는 a solution using T4 templates을 찾았지만 PostSharp를 사용하여 동일한 결과를 얻을 수 있는지 알고 싶습니다.

편집 :

명확하게하기 위해, 나는 이런 식으로 뭔가 쓸 수 있도록하고 싶습니다 :

[AutoEquatable] 
public class Thing 
{ 
    int Id { get; set; } 
    string Description { get; get; } 
} 

및 자동이로 변환 한 :

public class Thing 
{ 
    int Id { get; set; } 
    string Description { get; get; } 

    public override bool Equals(object other) 
    { 
     Thing o = other as Thing; 
     if (o == null) return false; 

     // generated in a loop based on the properties 
     if (!Id.Equals(o.Id)) return false; 
     if (!Description.Equals(o.Description)) return false; 

     return true; 
    } 
} 

답변

4

다음 코드를 사용하여 PostSharp 4.0에서 가능합니다.

[PSerializable] 
class EquatableAttribute : InstanceLevelAspect, IAdviceProvider 
{ 

    public List<ILocationBinding> Fields; 

    [ImportMember("Equals", IsRequired = true, Order = ImportMemberOrder.BeforeIntroductions)] 
    public Func<object, bool> EqualsBaseMethod; 


    [IntroduceMember(IsVirtual = true, OverrideAction = MemberOverrideAction.OverrideOrFail)] 
    public new bool Equals(object other) 
    { 
     // TODO: Define a smarter way to determine if base.Equals should be invoked. 
     if (this.EqualsBaseMethod.Method.DeclaringType != typeof(object)) 
     { 
      if (!this.EqualsBaseMethod(other)) 
       return false; 
     } 

     object instance = this.Instance; 
     foreach (ILocationBinding binding in this.Fields) 
     { 
      // The following code is inefficient because it boxes all fields. There is currently no workaround. 
      object thisFieldValue = binding.GetValue(ref instance, Arguments.Empty); 
      object otherFieldValue = binding.GetValue(ref other, Arguments.Empty); 

      if (!object.Equals(thisFieldValue, otherFieldValue)) 
       return false; 
     } 

     return true; 
    } 

    // TODO: Implement GetHashCode the same way. 

    public IEnumerable<AdviceInstance> ProvideAdvices(object targetElement) 
    { 
     Type targetType = (Type) targetElement; 
     FieldInfo bindingField = this.GetType().GetField("Fields"); 

     foreach (
      FieldInfo field in 
       targetType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | 
            BindingFlags.NonPublic)) 
     { 
      yield return new ImportLocationAdviceInstance(bindingField, new LocationInfo(field)); 
     } 
    } 

} 
+0

PostSharp 4에서이 기능에 대해 알지 못했습니다. 나는 PostSharp를 정말 좋아합니다. 어쨌든, 당신은 코드에서 리플렉션을 사용하고 있습니다 (예 : FieldInfo 등). 문제는 명시 적으로 refelction없이 나타납니다.;) – jlvaquero

+0

@ jlvaquero 동의하지만, (내포 된) 대답이 "당신은 반사없이 그것을 할 수 없다"면 그것은 나를 위해 충분합니다. 나는 반사 *없이 바람직하게 * 말했었다 ;-). 이 동작을 클래스에서 파생시켜야하는 단계입니다. 실험 할 코드가 있습니다. –

+0

리플렉션을 사용하지만 빌드시에만 사용합니다. 런타임에는 반영 할 필요가 없습니다. –

1

PostSharp으로는이 작업을 수행 할 수 없습니다. PostSharp "귀하의 clases에 aspect 코드를 삽입하지만 aspect를 코딩해야합니다. 여기서 핵심은 시스템에서 공통적 인 동작과 교차 절단 문제를 식별하고이를 Aspects으로 모델링하는 것입니다.

IIdentifiable의 예에서 GUID은 시스템의 여러 클래스에서 사용할 수있는 고유 한 식별자입니다. 그것은 일반적인 코드이며, 크로스 커팅 문제이며, 모든 클래스 엔티티에서 코드를 리플 팅하므로, Identificable은 Aspect로 모델링되어 반복되는 코드를 제거 할 수 있습니다.

다른 클래스가 같음 구현에서는 같음 구현을 "해독 (전환)"할 수 없습니다. 평등은 일반적인 행동이 아닙니다. 동등한 것은 교차하는 관심사가 아닙니다. 같음은 (반사없이) 화면이 될 수 없습니다.