基于类属性实现接口而不进行反射

时间:2014-09-23 22:06:44

标签: c# aop postsharp

PostSharp网站上的

This page有以下预告片:

  

您将遇到的一个常见情况是需要在大量类上实现特定接口。这可能是INotifyPropertyChangedIDisposeIEquatable或您创建的某个自定义界面。

我想编写一个自定义方面,它根据应用它的类的属性实现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;
    }
}

2 个答案:

答案 0 :(得分: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));
        }
    }

}

答案 1 :(得分:1)

我担心PostSharp无法做到这一点。 PostSharp&#34;注入&#34;您的clases中的方面代码但您必须对方面进行编码。这里的关键是在系统中解决常见行为和交叉问题,并将其建模为Aspects

IIdentifiable的示例中,您可以看到GUID如何是系统中许多不同类可以使用的唯一标识符。这是常见的代码,它是跨领域的关注点,您发现自己在所有类实体中重复代码,因此可以将Identificable建模为Aspect并摆脱重复代码。

由于不同的班级有不同的等级实施,你不能&#34; deatach&#34; (转换为方面)Equals的实现。等于不是一种常见的行为。平等不是跨领域的关注。等于不能是一个方面(没有反思)。