基于类属性实现接口而无需反思 [英] Implement interfaces based on class properties without reflection

查看:81
本文介绍了基于类属性实现接口而无需反思的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此页面具有以下预告片:

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

One of the common situations that you will encounter is the need to implement a specific interface on a large number of classes. This may be INotifyPropertyChanged, IDispose, IEquatable or some custom interface that you have created.

我想编写一个自定义方面,该方面基于所应用类的属性来实现IEquatable的常规版本(最好在编译时而不是在运行时使用反射).能够将属性添加到简单类而不是每次都必须实现自定义方法将是很好的.那可能吗?我希望如此,因为在本简介中专门对它进行了说明,但我一直无法找到任何示例代码.

I'd like to write a custom aspect that implements a general version of IEquatable based on the properties of the class it's applied to (preferably at compile-time instead of by using reflection at runtime). It would be good to just be able to add an attribute to a simple class rather than having to implement a custom method each time. Is that possible? I'd hope so, since it's specifically called out in this introduction, but I haven't been able to track down any example code.

我已经看到此示例,其中包含引入IIdentifiable界面的示例.但是它只返回一个GUID,它与新接口添加到的类无关.

I've seen this example from the PostSharp website that includes an example of introducing the IIdentifiable interface. But it just returns a GUID that's independent of the class that the new interface is added to.

是否有一种方法可以构造一个基于IEquatable的自定义属性,该属性基于所应用类型的属性(即,如果两个实例的所有属性都相等,则使两个实例相等)?

Is there a way to construct a custom attribute that implements IEquatable based on the properties of the type that it's applied to (i.e. making two instances equal if all of their properties are equal)?

我找到了使用T4模板的解决方案,但想知道是否可以使用T4模板实现相同的解决方案PostSharp.

I've found a solution using T4 templates but would like to know if the same can be achieved using PostSharp.

要清楚,我希望能够编写如下内容:

To be clear, I'd like to be able to write something like this:

[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;
    }
}

推荐答案

在PostSharp 4.0中,使用以下代码可以做到这一点;

This is possible with PostSharp 4.0 using the following code;

[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));
        }
    }

}

这篇关于基于类属性实现接口而无需反思的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆