基于类属性实现接口,无需反射 [英] Implement interfaces based on class properties without reflection
问题描述
此页面 PostSharp 网站上有以下预告:
<块引用>您会遇到的一种常见情况是需要在大量类上实现特定接口.这可能是 INotifyPropertyChanged
、IDispose
、IEquatable
或您创建的某些自定义界面.
我想编写一个自定义方面,它根据应用到的类的属性来实现 IEquatable
的一般版本(最好在编译时而不是在运行时使用反射).能够向简单的类添加属性而不是每次都实现自定义方法会很好.那可能吗?我希望如此,因为它在本介绍中特别提及,但我一直无法找到任何示例代码.
我见过 这个例子来自 PostSharp 网站,其中包括一个介绍 IIdentifiable
接口的例子.但它只返回一个 GUID
,它与添加新接口的类无关.
有没有一种方法可以根据所应用的类型的属性来构造实现 IEquatable
的自定义属性(即,如果两个实例的所有属性都相等,则使它们相等)?>
我找到了使用 T4 模板的解决方案,但想知道是否可以使用PostSharp.
明确地说,我希望能够写出这样的东西:
[AutoEquatable]公共课的东西{int Id { 获取;放;}字符串 描述 { 获取;得到;}}
并自动转换为:
公共类事物{int Id { 获取;放;}字符串 描述 { 获取;得到;}公共覆盖布尔等于(对象其他){事物 o = 其他事物;如果(o == null)返回false;//根据属性在循环中生成如果 (!Id.Equals(o.Id)) 返回 false;if (!Description.Equals(o.Description)) 返回假;返回真;}}
使用以下代码在 PostSharp 4.0 中可以做到这一点;
[PSerializable]类 EquatableAttribute : InstanceLevelAspect, IAdviceProvider{公共列表<ILocationBinding>领域;[ImportMember("Equals", IsRequired = true, Order = ImportMemberOrder.BeforeIntroductions)]public Func
This page on the PostSharp website has the following teaser:
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.
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.
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.
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)?
I've found a solution using T4 templates but would like to know if the same can be achieved using PostSharp.
Edit:
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; }
}
and have it automatically converted to this:
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;
}
}
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屋!