创建 class 以比较自身实例的最佳方法

Best way to create a class for comparing instances of itself

我正在编写一个程序,该程序从数据库中获取数据并将其逐值与旧数据进行比较,然后需要能够:

  1. 输出旧数据
  2. 输出新数据
  3. 只输出差异

因此,为了简单起见,假设我创建了以下 class 来存储值:

class MyClass
{
    // These fields will not be compared:
    public string Id { get; set; }
    public string Name { get; set; }
    public string NONCompField1 { get; set; }
    public int NONCompField2 { get; set; }

    // These fields WILL be compared:
    public string CompField1 { get; set; }
    public int CompField2 { get; set; }
    public int CompField3 { get; set; }
    public double CompField4 { get; set; }
}

并且,正如我的示例中的名称所示,我想比较旧值和新值,但仅基于 CompFields 来找出差异。

我知道我可以硬编码解决方案如下:

    public IEnumerable<Tuple<string, string, string>> GetDiffs(MyClass Other)
    {
        var RetVal = new List<Tuple<string, string, string>>();

        if (CompField1 != Other.CompField1)
            RetVal.Add(Tuple.Create("CompField1", CompField1, Other.CompField1));
        if (CompField2 != Other.CompField2)
            RetVal.Add(Tuple.Create("CompField2", CompField2.ToString(), Other.CompField2.ToString()));
        if (CompField3 != Other.CompField3)
            RetVal.Add(Tuple.Create("CompField3", CompField3.ToString(), Other.CompField3.ToString()));
        if (CompField4 != Other.CompField4)
            RetVal.Add(Tuple.Create("CompField4", CompField4.ToString(), Other.CompField4.ToString()));

        return RetVal;
    }

这给了我一个 Tuple<string, string, string> of (Field_Name, Current_Value, Old_Value) 并且效果很好,但我正在寻找更好的, 模式动态方式来做到这一点,这将允许在未来添加新字段 - 无论它们是 CompareFields(需要更新 GetDiffs)还是 NONCompFields(不需要更新GetDiffs).

我的第一个想法是使用 DataAnnotations 然后反射来查找具有特定属性的字段并循环遍历它们以获得 GetDiffs 方法,但这似乎是一种作弊/错误的方法做事

是否有更好/更成熟的方法可以最大限度地减少更新额外代码的需要?

谢谢!!!

创建一个委托列表,将项目投影到您要比较的每个不同属性,然后比较两个项目上每个投影的结果:

public IEnumerable<Tuple<string, string, string>> GetDiffs(MyClass Other)
{
    var comparisons = new[] 
    {
        Tuple.Create<string, Func<MyClass, object>>("CompField1", item => item.CompField1),
        Tuple.Create<string, Func<MyClass, object>>("CompField2", item => item.CompField2),
        Tuple.Create<string, Func<MyClass, object>>("CompField3", item => item.CompField3),
        Tuple.Create<string, Func<MyClass, object>>("CompField4", item => item.CompField4),
    };

    return from comparison in comparisons
            let myValue = comparison.Item2(this)
            let otherValue = comparison.Item2(Other)
            where !object.Equals(myValue, otherValue)
            select Tuple.Create(comparison.Item1,
                myValue.ToString(),
                otherValue.ToString());
}

仅供参考 - 对于任何其他想要与我在这里所做的相同的人,我最终修改了我找到的代码 here

这允许我做的是传入一个 属性 名称列表以用于比较(尽管该示例做相反的事情 - 它有一个要忽略的名称列表,但这是一个简单的更改) 否则修改非常简单。

希望这至少能帮助指导处于相同情况的其他人。