查找两个 C# 对象之间的属性差异

2024-03-04

我正在开发的项目需要在用户更改电子邮件、帐单地址等时进行一些简单的审核日志记录。我们正在使用的对象来自不同的来源,一个是 WCF 服务,另一个是 Web 服务。

我已经使用反射实现了以下方法来查找两个不同对象上属性的更改。这会生成一个具有差异的属性列表及其旧值和新值。

public static IList GenerateAuditLogMessages(T originalObject, T changedObject)
{
    IList list = new List();
    string className = string.Concat("[", originalObject.GetType().Name, "] ");

    foreach (PropertyInfo property in originalObject.GetType().GetProperties())
    {
        Type comparable =
            property.PropertyType.GetInterface("System.IComparable");

        if (comparable != null)
        {
            string originalPropertyValue =
                property.GetValue(originalObject, null) as string;
            string newPropertyValue =
                property.GetValue(changedObject, null) as string;

            if (originalPropertyValue != newPropertyValue)
            {
                list.Add(string.Concat(className, property.Name,
                    " changed from '", originalPropertyValue,
                    "' to '", newPropertyValue, "'"));
            }
        }
    }

    return list;
}

我正在寻找 System.IComparable,因为“所有数字类型(例如 Int32 和 Double)都实现 IComparable,String、Char 和 DateTime 也是如此。”这似乎是查找非自定义类的任何属性的最佳方法。

利用 WCF 或 Web 服务代理代码生成的 PropertyChanged 事件听起来不错,但没有为我的审核日志(旧值和新值)提供足够的信息。

寻求意见,是否有更好的方法来做到这一点,谢谢!

@Aaronaught,这里是一些基于执行 object.Equals 生成正匹配的示例代码:

Address address1 = new Address();
address1.StateProvince = new StateProvince();

Address address2 = new Address();
address2.StateProvince = new StateProvince();

IList list = Utility.GenerateAuditLogMessages(address1, address2);

“[地址] StateProvince 更改自 'MyAccountService.StateProvince' 至 'MyAccountService.StateProvince'"

它是 StateProvince 类的两个不同实例,但属性值相同(在本例中均为 null)。我们不会重写 equals 方法。


IComparable用于排序比较。要么使用IEquatable相反,或者只使用静态System.Object.Equals方法。后者的好处是,如果对象不是原始类型,但仍然通过重写来定义自己的相等比较,则也可以工作Equals.

object originalValue = property.GetValue(originalObject, null);
object newValue = property.GetValue(changedObject, null);
if (!object.Equals(originalValue, newValue))
{
    string originalText = (originalValue != null) ?
        originalValue.ToString() : "[NULL]";
    string newText = (newText != null) ?
        newValue.ToString() : "[NULL]";
    // etc.
}

这显然并不完美,但如果您只使用您控制的类来执行此操作,那么您可以确保它始终满足您的特定需求。

还有其他方法可以比较对象(例如校验和、序列化等),但如果类没有一致实现,这可能是最可靠的IPropertyChanged并且您想真正了解其中的差异。


更新新的示例代码:

Address address1 = new Address();
address1.StateProvince = new StateProvince();

Address address2 = new Address();
address2.StateProvince = new StateProvince();

IList list = Utility.GenerateAuditLogMessages(address1, address2);

使用的原因object.Equals在您的审核方法中结果“命中”是因为实例实际上不相等!

当然,StateProvince在这两种情况下都可能为空,但是address1 and address2仍然有非空值StateProvince属性和每个实例都是不同的。所以,address1 and address2具有不同的属性。

让我们翻转过来,以这段代码为例:

Address address1 = new Address("35 Elm St");
address1.StateProvince = new StateProvince("TX");

Address address2 = new Address("35 Elm St");
address2.StateProvince = new StateProvince("AZ");

这些应该被视为平等吗?嗯,他们会的,用你的方法,因为StateProvince不执行IComparable。这是您的方法报告两个对象在原始情况下相同的唯一原因。自从StateProvince类没有实现IComparable,跟踪器完全跳过该属性。但这两个地址显然不相等!

这就是为什么我最初建议使用object.Equals,因为这样你就可以在StateProvince获得更好结果的方法:

public class StateProvince
{
    public string Code { get; set; }

    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;

        StateProvince sp = obj as StateProvince;
        if (object.ReferenceEquals(sp, null))
            return false;

        return (sp.Code == Code);
    }

    public bool Equals(StateProvince sp)
    {
        if (object.ReferenceEquals(sp, null))
            return false;

        return (sp.Code == Code);
    }

    public override int GetHashCode()
    {
        return Code.GetHashCode();
    }

    public override string ToString()
    {
        return string.Format("Code: [{0}]", Code);
    }
}

完成此操作后,object.Equals代码将完美运行。而不是天真地检查是否address1 and address2字面上有相同的StateProvince参考,它实际上会检查语义相等性。


另一种方法是扩展跟踪代码以实际下降到子对象。换句话说,对于每个属性,检查Type.IsClass以及可选的Type.IsInterface财产,并且如果true,然后递归地调用属性本身的更改跟踪方法,并使用属性名称作为递归返回的任何审核结果的前缀。所以你最终会得到改变StateProvinceCode.

我有时也使用上述方法,但覆盖更容易Equals在您想要比较语义相等性(即审计)的对象上并提供适当的ToStringoverride 可以清楚地表明发生了什么变化。它不能扩展深层嵌套,但我认为以这种方式进行审核是不寻常的。

最后一个技巧是定义您自己的接口,例如IAuditable<T>,它采用相同类型的第二个实例作为参数,并实际上返回所有差异的列表(或可枚举)。和我们的重写类似object.Equals上面的方法但返回更多信息。当对象图非常复杂并且您知道不能依赖反射或Equals。您可以将此与上述方法结合起来;实际上你所要做的就是替代IComparable为您IAuditable并调用Audit方法(如果它实现了该接口)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

查找两个 C# 对象之间的属性差异 的相关文章

随机推荐

  • 标记模板文字的 TemplateObject 数组是否被其领域弱引用?

    while c tag str0 e str1 JavaScript 运行时创建一个冻结数组 例如Object freeze str0 str1 但还有一个额外的 raw财产 可以使用该对象作为 a 中的键吗 WeakMap以避免每次循环时
  • 需要 Angular 2 ng

    我在 Angular 2 中制作了一个模型驱动表单 并且只有在未选中上面的复选框时才必须显示其中一个输入字段 我使用 ngIf 执行此操作 我的问题是 仅当未选中该复选框时 如何设置所需的输入 在 Angular 1 x 中 我可以通过视图
  • 递归 XSLT,第 2 部分

    好的 继续我的问题here https stackoverflow com questions 2907332 how can i write an xslt that will recursively include other file
  • 经常将 IRB 历史记录刷新到文件

    irb 可以将命令历史记录写入文件 但只有在 irb 会话结束时才会执行此操作 我想更频繁地写出我的命令历史记录 与每个命令 如 shell 历史记录 一样频繁 但不必如此频繁 是否有 irbrc 设置 或者我必须破解 irb 源 已经多次
  • 将鼠标悬停在图像上方的 Font Awesome 图标居中

    当鼠标悬停在图像上时 我试图将字体很棒的图标置于图像的中心 这是我的 HTML div class profile img container img src http s3 amazonaws com 37assets svn 765 d
  • 如何在 Xamarin Forms 中将 Android 日期选择器设置为微调器(而非日历)模式?

    在 Android 设备上的 Xamarin Forms 中选择日期选择器控件时 将显示日期选择器的日历模式视图 如何更改它以显示微调模式 我找到的唯一示例是更新我的 styles xml 这是我所拥有的 但不起作用
  • CSS 不同的链接样式

    据我所知 使用类似的东西 class a 与使用相同 class a link Right 另外 以下两种款式是一样的吗 class a focus class a hover 最后 下面的样式是鼠标按下时的颜色吧 class a acti
  • Zend Framework 2 - Hydrator 策略未响应且水合

    我基本上实现了这个策略 https github com zendframework zf2 pull 2072 主要区别是 我猜 我使用Doctrine2 调用构造函数类 打印测试回显 但两个函数extract and hydrate 不
  • LINQ:将 lambda 表达式作为参数传递以由方法执行和返回

    因此 场景如下 我有一系列不同的存储库类 每个类都可以使用独立的数据上下文或共享上下文 在使用隔离上下文的情况下 我想向基类添加一个方法 该方法允许我将 lambda 指定为参数 让该表达式由所选存储库的隔离上下文执行并返回 IQuerya
  • 如何设置 Bitmap.Width 和 Bitmap.height

    您好 我已经加载了位图 我需要设置自己的高度和宽度 bitmap height 100 但这个声明不允许我 因为它说 System Drawing Image Width cannot be assigned to it is read o
  • __del__() 如何干扰垃圾回收?

    I read class Account object def init self name balance self name name self balance balance self observers set def del se
  • 如何从 Jenkins 推送到 git?

    以下代码是 Jenkins 中的 执行 Shell 构建步骤 该作业从包含文件的存储库中提取ranger policies policies json 我想要做的是更新该文件 在本例中使用curl 命令 然后将更改提交到源代码管理并更新远程
  • Android模拟器中的相机

    我使用 android SDK 4 0 3 和 eclipse Indigo 我正在尝试开发一个使用相机的应用程序 当我拍照时 会出现以下消息 不幸的是 相机已经停止了 在 Eclipse 控制台中 模拟器 错误 camera client
  • 使用端口(例如 localhost:12345)访问 IPv6 可解析 URL 会导致 Strawberry Perl 5.30.1 中的错误地址

    在启用了 IPv6 的 Windows 10 下使用 Strawberry Perl 5 30 1 时 由于 Perl 的 DNS 解析器中似乎存在错误 因此无法正确解析带有端口号的 URL 对于以下测试 我们有一个简单的 Web 服务器
  • 测试类型是否是分配器的正确方法是什么?

    Writing allocator traits
  • 将可扩展性设计到应用程序中

    这句话是什么意思 将可扩展性设计到应用程序中 是否存在可以使应用程序更具可扩展性的设计模式 这个问题主要针对 Web 应用程序或基于 SOA 中间件的应用程序 当我想到 大规模应用程序 时 我想到了三个截然不同的事情 将在大型横向扩展集群
  • 使用 C# 计算每个句子中的单词数

    我需要创建一个程序来显示单词最多的句子 string st I like apples I like red apples I like red apples than green apples foreach string s in st
  • LINQ to SQL 与 ADO.Net

    LINQ to SQL 和 ADO net 之间有什么区别 ADO NET 是 NET Framework 的底层数据访问 API 很像 Java 中的 JDBC 它自 NET 第一个版本发布以来就已存在 LINQ to SQL 是一个基于
  • 连续 2d 切片上的 Numpy 向量化函数

    我有一个 3D numpy 数组 我想通过在沿轴的连续 2d 切片上执行函数并将生成的切片堆叠在一起来形成一个新的 3d 数组 显然有很多方法可以做到这一点 我想以最简洁的方式做到这一点 我认为这是可能的numpy vectorize 但这
  • 查找两个 C# 对象之间的属性差异

    我正在开发的项目需要在用户更改电子邮件 帐单地址等时进行一些简单的审核日志记录 我们正在使用的对象来自不同的来源 一个是 WCF 服务 另一个是 Web 服务 我已经使用反射实现了以下方法来查找两个不同对象上属性的更改 这会生成一个具有差异