考虑以下场景:
public interface ICloneable<T>:
ICopyable<T> where T : ICloneable<T>, new()
{
public T CloneTyped ();
}
public interface ICopyable<T>
where T : ICopyable<T>
{
public T CopyTo (T other);
public T CopyFrom (T other);
}
public interface IDirtyable<T>
where T : IDirtyable<T>
{
// Compares the current object [this] to an unaltered object [referenceObject].
public bool IsDirty (T referenceObject);
}
public partial class Model:
ICloneable<Model>,
ICopyable<Model>,
IDirtyable<Model>
{
public virtual long Id { get; set; }
public virtual DateTime DateTimeCreated { get; set; }
public virtual DateTime DateTimeModified { get; set; }
public virtual string Name { get; set; } = string.Empty;
public Model CloneTyped () => new Model().CopyFrom(this);
public Model CopyFrom (Model other) => other.CopyTo(this);
public Model CopyTo (Model other)
{
other.Id = this.Id;
other.DateTimeCreated = this.DateTimeCreated;
other.DateTimeModified = this.DateTimeModified;
other.Name = this.Name;
return (other);
}
public bool IsDirty (Model referenceObject)
{
return
this.Id == referenceObject.Id
&& this.DateTimeCreated == referenceObject.DateTimeCreated
&& this.DateTimeModified == referenceObject.DateTimeModified
&& this.Name == referenceObject.Name;
}
}
请注意,就这个问题而言,我对
INotifyPropertyChanged
或其他类似机制不感兴趣。此外,假设类 Model
有数十个标量属性(我们不关心复合类型)。
因此我们留下了一个接口模式,对两个相同类型的对象执行比较或赋值操作。现在,因为我知道这些接口实现应该处理的属性列表,所以我可以执行如下操作:
public bool IsDirtyUsingReflection (Model referenceObject)
{
var equal = true;
var propertyNames = new [] { nameof(this.Id), nameof(this.DateTimeCreated), nameof(this.DateTimeModified), nameof(this.Name), };
var properties = this.GetType().GetProperties().Where(p => propertyNames.Contains(p.Name));
foreach (var property in properties)
{
equal
&= property.GetValue(this)
?.Equals(property.GetValue(referenceObject))
?? false;
if (!equal) { return (true); }
}
return (false);
}
所以,问题是,我如何维护属性列表并在没有反射或装箱的情况下比较/分配它们?
我考虑过维护
Expression
类型等属性。
var properties = new Expression<Func<T, object?>> [] {...};
任何指导将不胜感激以实现以下目标:
static
属性列表。Expression
对象或编译的 lambda 列表。在类型级别 (static1
) 进行编译和/或装箱就可以了。要考虑的数据类型仅包括内置值类型、字符串、枚举和实现
IEquitable
的结构。
表达式生成或原始引用发射(
ILGenerator
)确实是可行的,但是:我想知道是否更好的方法是编写一个 Roslyn 代码生成器来为您执行此操作。例如,您可以写:
public partial class Model
{
public partial bool IsDirty (Model referenceObject);
}
生成器可以检测并根据构建期间发现的成员输出缺失的一半:
partial class Model
{
public partial bool IsDirty (Model referenceObject)
{
return
this.Id == referenceObject.Id
&& this.DateTimeCreated == referenceObject.DateTimeCreated
&& this.DateTimeModified == referenceObject.DateTimeModified
&& this.Name == referenceObject.Name;
}
}
也许需要更多工作,但从长远来看,它可以被冻结得更加灵活和高性能 - 维护表达式/引用发出代码非常困难(问我怎么知道......),在哪里进行调整到发电机输出:简单。