2014-09-23 66 views
4

This page有以下傳情:實現基於類的屬性接口沒有PostSharp網站上反射

其中一個,你會遇到的常見情況是實現對大量的類特定接口的需要。這可能是INotifyPropertyChanged,IDispose,IEquatable或您創建的一些自定義界面。

我想編寫實現基於要應用的(優選在編譯時,而不是使用反射在運行時)的類的屬性的IEquatable一般版本的自定義的方面。只要能夠將屬性添加到簡單的類中,而不是每次都需要實現自定義方法,這將是一件好事。那可能嗎?我希望如此,因爲它在本文中特別提到,但我一直無法找到任何示例代碼。

我曾見過PostSharp網站的this example,其中包括一個介紹IIdentifiable界面的例子。但是它只是返回一個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等等),問題是關於沒有明確的反射。;) – jlvaquero 2014-09-25 05:53:24

+0

@jlvaquero同意,但如果(暗示的)答案是「你不能沒有反思就做到這一點」,那對我來說就夠了。我確實說*優先*沒有反思;-)。將這種行爲添加到一個不得不從中派生出來的類仍然是一個步驟,它給了我一些實驗代碼。 – 2014-09-25 12:50:20

+0

我使用反射,但只在構建時。運行時不涉及反射。 – 2014-09-26 06:33:58

1

恐怕這不能用PostSharp完成。 PostSharp「注入」方面的代碼,但你必須編碼的方面。這裏的關鍵是確定系統中的常見行爲和交叉關注點,並將其模型化爲Aspects

IIdentifiable的示例中,您可以看到GUID是一個可供系統中許多不同類使用的唯一標識符。這是常見的代碼,它是橫切關注的問題,您可以在所有類實體中找到REPEATING代碼,以便Identificable可以建模爲Aspect並擺脫重複代碼。

由於不同的類有不同的Equals實現,所以不能「去除」(轉換爲方面)Equals的實現。等於不是一種常見的行爲。平等不是交叉擔憂。等於不能成爲一個方面(沒有反思)。