在没有反射的情况下实现基于类属性的接口
本文关键字:于类 属性 接口 实现 反射的 情况下 | 更新日期: 2023-09-27 18:24:04
PostSharp网站上的这个页面有以下预告片:
您将遇到的常见情况之一是需要在大量类上实现特定的接口。这可能是
INotifyPropertyChanged
、IDispose
、IEquatable
或您创建的某个自定义接口。
我想写一个自定义方面,根据应用到的类的属性实现IEquatable
的通用版本(最好是在编译时,而不是在运行时使用反射)。只要能够为一个简单的类添加一个属性就好了,而不必每次都实现一个自定义方法。这可能吗?我希望如此,因为它在引言中有明确的说明,但我还没有找到任何示例代码。
我在PostSharp网站上看到了这个例子,其中包括一个介绍IIdentifiable
接口的例子。但它只是返回一个GUID
,它与新接口添加到的类无关
有没有一种方法可以构造一个自定义属性,该属性基于应用到的类型的属性来实现IEquatable
(即,如果两个实例的所有属性都相等,则使它们相等)?
我已经找到了一个使用T4模板的解决方案,但我想知道是否可以使用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;
}
}
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));
}
}
}
恐怕PostSharp
无法做到这一点。PostSharp
在类中"注入"方面代码,但必须对方面进行编码。这里的关键是确定系统中的常见行为和交叉关注点,并将其建模为Aspects
。
在IIdentifiable
的示例中,您可以看到GUID
是一个唯一的标识符,可以被系统中的许多不同类使用。这是常见的代码,是一个跨领域的问题,您发现自己在所有类实体中都在重复代码,因此Identificable
可以建模为Aspect并消除重复代码。
由于不同的类有不同的Equals实现,您不能"deatach"(转换为方面)Equals的实现。等于不是一种常见的行为。平等不是贯穿各领域的问题。Equals不能是Aspect(没有反射)。